home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / vm / vmPage.c < prev    next >
C/C++ Source or Header  |  1991-07-30  |  80KB  |  3,010 lines

  1. /* 
  2.  * vmPage.c --
  3.  *
  4.  *      This file contains routines that manage the core map, allocate
  5.  *      list, free list, dirty list and reserve page list.  The core map
  6.  *    contains one entry for each page frame in physical memory.  The four
  7.  *    lists run through the core map.  The dirty list contains all pages
  8.  *    that are being written to disk.  Dirty pages are written out by
  9.  *    a set of pageout processes.  Pages are put onto the dirty list by the
  10.  *    page allocation routine.  The allocate list contains all pages that
  11.  *    are being used by user processes and are not on the dirty list.  It is
  12.  *    kept in approximate LRU order by a version of the clock algorithm. 
  13.  *    The free list contains pages that aren't being used by any user
  14.  *    processes or the kernel.  The reserve list is a few pages
  15.  *    that are set aside for emergencies when the kernel needs memory but 
  16.  *    all of memory is dirty.
  17.  *
  18.  *    LOCKING PAGES
  19.  *
  20.  *    In general all pages that are on the allocate page list are eligible 
  21.  *    to be given to any process.  However, if a page needs to be locked down
  22.  *    so that it cannot be taken away from its owner, there is a lock count
  23.  *    field in the core map entry for a page frame to allow this.  As long
  24.  *    as the lock count is greater than zero, the page cannot be taken away
  25.  *    from its owner.
  26.  *    
  27.  * Copyright (C) 1985 Regents of the University of California
  28.  * All rights reserved.
  29.  */
  30.  
  31. #ifndef lint
  32. static char rcsid[] = "$Header: /sprite/src/kernel/vm/RCS/vmPage.c,v 9.18 91/07/30 16:41:24 shirriff Exp $ SPRITE (Berkeley)";
  33. #endif not lint
  34.  
  35. #include <sprite.h>
  36. #include <vmStat.h>
  37. #include <vm.h>
  38. #include <vmInt.h>
  39. #include <vmTrace.h>
  40. #include <vmSwapDir.h>
  41. #include <user/vm.h>
  42. #include <sync.h>
  43. #include <dbg.h>
  44. #include <list.h>
  45. #include <timer.h>
  46. #include <lock.h>
  47. #include <sys.h>
  48. #include <fscache.h>
  49. #include <fsio.h>
  50. #include <fsrmt.h>
  51. #include <stdio.h>
  52.  
  53. Boolean    vmDebug    = FALSE;
  54.  
  55. static    VmCore          *coreMap;    /* Pointer to core map that is 
  56.                        allocated in VmCoreMapAlloc. */
  57.  
  58. extern int debugVmStubs; /* Unix compatibility debug flag. */
  59.  
  60. /*
  61.  * Minimum fraction of pages that VM wants for itself.  It keeps
  62.  * 1 / MIN_VM_PAGE_FRACTION of the available pages at boot time for itself.
  63.  */
  64. #define    MIN_VM_PAGE_FRACTION    16
  65.  
  66. /*
  67.  * Variables to define the number of page procs working at a time and the
  68.  * maximum possible number that can be working at a time.
  69.  */
  70. static    int    numPageOutProcs = 0;
  71. int        vmMaxPageOutProcs = VM_MAX_PAGE_OUT_PROCS;
  72.  
  73. /*
  74.  * Page lists.  There are four different lists and a page can be on at most
  75.  * one list.  The allocate list is a list of in use pages that is kept in
  76.  * LRU order. The dirty list is a list of in use pages that are being
  77.  * written to swap space.  The free list is a list of pages that are not
  78.  * being used by any process.  The reserve list is a list with
  79.  * NUM_RESERVE_PAGES on it that is kept for the case when the kernel needs
  80.  * new memory but all of memory is dirty.
  81.  */
  82. #define    NUM_RESERVE_PAGES    3
  83. static    List_Links      allocPageListHdr;
  84. static    List_Links      dirtyPageListHdr;
  85. static    List_Links    freePageListHdr;
  86. static    List_Links    reservePageListHdr;
  87. #define allocPageList    (&allocPageListHdr)
  88. #define dirtyPageList    (&dirtyPageListHdr)
  89. #define    freePageList    (&freePageListHdr)
  90. #define    reservePageList    (&reservePageListHdr)
  91.  
  92. /*
  93.  * Condition to wait for a clean page to be put onto the allocate list.
  94.  */
  95. Sync_Condition    cleanCondition;    
  96.  
  97. /*
  98.  * Variables to allow recovery.
  99.  */
  100. static    Boolean        swapDown = FALSE;
  101. Sync_Condition    swapDownCondition;
  102.  
  103. /*
  104.  * Maximum amount of pages that can be on the dirty list before waiting for
  105.  * a page to be cleaned.  It is a function of the amount of free memory at
  106.  * boot time.
  107.  */
  108. #define    MAX_DIRTY_PAGE_FRACTION    4
  109. int    vmMaxDirtyPages;
  110.  
  111. Boolean    vmFreeWhenClean = TRUE;    
  112. Boolean    vmAlwaysRefuse = FALSE;    
  113. Boolean    vmAlwaysSayYes = FALSE;    
  114. Boolean    vmWriteablePageout = FALSE;
  115. Boolean vmWriteableRefPageout = FALSE;
  116.  
  117. int    vmFSPenalty = 0;
  118. int    vmNumPageGroups = 10;
  119. int    vmPagesPerGroup;
  120. int    vmCurPenalty;
  121. int    vmBoundary;
  122. Boolean    vmCORReadOnly = FALSE;
  123.  
  124. /*
  125.  * Limit to put on the number of pages the machine can have.  Used for
  126.  * benchmarking purposes only.
  127.  */
  128. int    vmPhysPageLimit = -1;
  129.  
  130. static void PageOut _ARGS_((ClientData data, Proc_CallInfo *callInfoPtr));
  131. static void PutOnReserveList _ARGS_((register VmCore *corePtr));
  132. static void PutOnFreeList _ARGS_((register VmCore *corePtr));
  133.  
  134.  
  135. /*
  136.  * ----------------------------------------------------------------------------
  137.  *
  138.  * VmCoreMapAlloc --
  139.  *
  140.  *         Allocate space for the core map.
  141.  *
  142.  * Results:
  143.  *         None.
  144.  *
  145.  * Side effects:
  146.  *    Core map allocated.
  147.  * ----------------------------------------------------------------------------
  148.  */
  149. void
  150. VmCoreMapAlloc()
  151. {
  152.     if (vmPhysPageLimit > 0 && vmPhysPageLimit < vmStat.numPhysPages) {
  153.     vmStat.numPhysPages = vmPhysPageLimit;
  154.     }
  155.     printf("Available memory %d\n", vmStat.numPhysPages * vm_PageSize);
  156.     coreMap = (VmCore *) Vm_BootAlloc(sizeof(VmCore) * vmStat.numPhysPages);
  157.     bzero((char *) coreMap, sizeof(VmCore) * vmStat.numPhysPages);
  158. }
  159.  
  160.  
  161. /*
  162.  * ----------------------------------------------------------------------------
  163.  *
  164.  * VmCoreMapInit --
  165.  *
  166.  *         Initialize the core map.
  167.  *
  168.  * Results:
  169.  *         None.
  170.  *
  171.  * Side effects:
  172.  *    Core map initialized.
  173.  * ----------------------------------------------------------------------------
  174.  */
  175. void
  176. VmCoreMapInit()
  177. {
  178.     register    int    i;
  179.     register    VmCore    *corePtr;
  180.     int            firstKernPage;
  181.  
  182.     /*   
  183.      * Initialize the allocate, dirty, free and reserve lists.
  184.      */
  185.     List_Init(allocPageList);
  186.     List_Init(dirtyPageList);
  187.     List_Init(freePageList);
  188.     List_Init(reservePageList);
  189.  
  190.     firstKernPage = (unsigned int)mach_KernStart >> vmPageShift;
  191.     /*
  192.      * Initialize the core map.  All pages up to vmFirstFreePage are
  193.      * owned by the kernel and the rest are free.
  194.      */
  195.     for (i = 0, corePtr = coreMap; i < vmFirstFreePage; i++, corePtr++) {
  196.     corePtr->links.nextPtr = (List_Links *) NIL;
  197.     corePtr->links.prevPtr = (List_Links *) NIL;
  198.         corePtr->lockCount = 1;
  199.     corePtr->wireCount = 0;
  200.         corePtr->flags = 0;
  201.         corePtr->virtPage.segPtr = vm_SysSegPtr;
  202.         corePtr->virtPage.page = i + firstKernPage;
  203.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  204.     }
  205.     /*
  206.      * The first NUM_RESERVED_PAGES are put onto the reserve list.
  207.      */
  208.     for (i = vmFirstFreePage, vmStat.numReservePages = 0;
  209.          vmStat.numReservePages < NUM_RESERVE_PAGES;
  210.      i++, corePtr++) {
  211.     corePtr->links.nextPtr = (List_Links *) NIL;
  212.     corePtr->links.prevPtr = (List_Links *) NIL;
  213.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  214.     PutOnReserveList(corePtr);
  215.     }
  216.     /*
  217.      * The remaining pages are put onto the free list.
  218.      */
  219.     for (vmStat.numFreePages = 0; i < vmStat.numPhysPages; i++, corePtr++) {
  220.     corePtr->links.nextPtr = (List_Links *) NIL;
  221.     corePtr->links.prevPtr = (List_Links *) NIL;
  222.     corePtr->virtPage.sharedPtr = (Vm_SegProcList *) NIL;
  223.     PutOnFreeList(corePtr);
  224.     }
  225. }
  226.  
  227.  
  228. /*
  229.  * ----------------------------------------------------------------------------
  230.  *
  231.  *    Routines to manage the four lists.
  232.  *
  233.  * ----------------------------------------------------------------------------
  234.  */
  235.  
  236. /*
  237.  * ----------------------------------------------------------------------------
  238.  *
  239.  * PutOnAllocListFront --
  240.  *
  241.  *         Put this core map entry onto the front of the allocate list.  
  242.  *
  243.  * Results:
  244.  *         None.
  245.  *
  246.  * Side effects:
  247.  *    Alloc lists modified and core map entry modified.
  248.  * ----------------------------------------------------------------------------
  249.  */
  250. INTERNAL static void
  251. PutOnAllocListFront(corePtr)
  252.     register    VmCore    *corePtr;
  253. {
  254.     VmListInsert((List_Links *) corePtr, LIST_ATFRONT(allocPageList));
  255.     vmStat.numUserPages++;
  256. }
  257.  
  258.  
  259. /*
  260.  * ----------------------------------------------------------------------------
  261.  *
  262.  * PutOnAllocListRear --
  263.  *
  264.  *         Put this core map entry onto the rear of the allocate list
  265.  *
  266.  * Results:
  267.  *         None.
  268.  *
  269.  * Side effects:
  270.  *    Alloc list modified and core map entry modified.
  271.  * ----------------------------------------------------------------------------
  272.  */
  273. INTERNAL static void
  274. PutOnAllocListRear(corePtr)
  275.     VmCore    *corePtr;
  276. {
  277.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  278.     vmStat.numUserPages++;
  279. }
  280.  
  281.  
  282. /*
  283.  * ----------------------------------------------------------------------------
  284.  *
  285.  * PutOnAllocList --
  286.  *
  287.  *         Put the given core map entry onto the end of the allocate list.
  288.  *
  289.  * Results:
  290.  *         None.
  291.  *
  292.  * Side effects:
  293.  *         Core map entry put onto end of allocate list.
  294.  *
  295.  * ----------------------------------------------------------------------------
  296.  */
  297.  
  298. ENTRY static void
  299. PutOnAllocList(virtAddrPtr, page)
  300.     Vm_VirtAddr        *virtAddrPtr;    /* The translated virtual address that 
  301.                      * indicates the segment and virtual
  302.                      * page that this physical page is
  303.                      * being allocated for */
  304.     unsigned int    page;
  305. {
  306.     register    VmCore    *corePtr; 
  307.     Time        curTime;
  308.  
  309.     LOCK_MONITOR;
  310.  
  311.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  312.  
  313.     corePtr = &coreMap[page];
  314.  
  315.     /*
  316.      * Move the page to the end of the allocate list and initialize the core 
  317.      * map entry.  If page is for a kernel process then don't put it onto
  318.      * the end of the allocate list.
  319.      */
  320.     if (virtAddrPtr->segPtr != vm_SysSegPtr) {
  321.     PutOnAllocListRear(corePtr);
  322.     }
  323.  
  324.     corePtr->virtPage = *virtAddrPtr;
  325.     corePtr->flags = 0;
  326.     corePtr->lockCount = 1;
  327.     corePtr->lastRef = curTime.seconds;
  328.  
  329.     UNLOCK_MONITOR;
  330. }
  331.  
  332.  
  333. /*
  334.  * ----------------------------------------------------------------------------
  335.  *
  336.  * TakeOffAllocList --
  337.  *
  338.  *         Take this core map entry off of the allocate list
  339.  *
  340.  * Results:
  341.  *         None.
  342.  *
  343.  * Side effects:
  344.  *    Alloc list modified and core map entry modified.
  345.  * ----------------------------------------------------------------------------
  346.  */
  347. INTERNAL static void
  348. TakeOffAllocList(corePtr)
  349.     VmCore    *corePtr;
  350. {
  351.     VmListRemove((List_Links *) corePtr);
  352.     vmStat.numUserPages--;
  353. }
  354.  
  355.  
  356. /*
  357.  * ----------------------------------------------------------------------------
  358.  *
  359.  * PutOnReserveList --
  360.  *
  361.  *         Put this core map entry onto the reserve page list.
  362.  *
  363.  * Results:
  364.  *         None.
  365.  *
  366.  * Side effects:
  367.  *    Reserve list modified and core map entry modified.
  368.  * ----------------------------------------------------------------------------
  369.  */
  370. INTERNAL static void
  371. PutOnReserveList(corePtr)
  372.     register    VmCore    *corePtr;
  373. {
  374.     corePtr->flags = 0;
  375.     corePtr->lockCount = 1;
  376.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(reservePageList));
  377.     vmStat.numReservePages++;
  378. }
  379.  
  380.  
  381. /*
  382.  * ----------------------------------------------------------------------------
  383.  *
  384.  * VmGetReservePage --
  385.  *
  386.  *         Take a core map entry off of the reserve list and return its 
  387.  *    page frame number.
  388.  *
  389.  * Results:
  390.  *         None.
  391.  *
  392.  * Side effects:
  393.  *    Reserve list modified and core map entry modified.
  394.  * ----------------------------------------------------------------------------
  395.  */
  396. INTERNAL unsigned int
  397. VmGetReservePage(virtAddrPtr)
  398.     Vm_VirtAddr    *virtAddrPtr;
  399. {
  400.     VmCore    *corePtr;
  401.  
  402.     if (List_IsEmpty(reservePageList)) {
  403.     return(VM_NO_MEM_VAL);
  404.     }
  405.     printf("Taking from reserve list\n");
  406.     vmStat.reservePagesUsed++;
  407.     corePtr = (VmCore *) List_First(reservePageList);
  408.     List_Remove((List_Links *) corePtr);
  409.     vmStat.numReservePages--;
  410.     corePtr->virtPage = *virtAddrPtr;
  411.  
  412.     return(corePtr - coreMap);
  413. }
  414.  
  415.  
  416. /*
  417.  * ----------------------------------------------------------------------------
  418.  *
  419.  * PutOnFreeList --
  420.  *
  421.  *         Put this core map entry onto the free list.  The page will actually
  422.  *    end up on the reserve list if the reserve list needs more pages.
  423.  *
  424.  * Results:
  425.  *         None.
  426.  *
  427.  * Side effects:
  428.  *    Free list or reserve list modified and core map entry modified.
  429.  * ----------------------------------------------------------------------------
  430.  */
  431. INTERNAL static void
  432. PutOnFreeList(corePtr)
  433.     register    VmCore    *corePtr;
  434. {
  435.     if (vmStat.numReservePages < NUM_RESERVE_PAGES) {
  436.     PutOnReserveList(corePtr);
  437.     } else {
  438.     corePtr->flags = VM_FREE_PAGE;
  439.     corePtr->lockCount = 0;
  440.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(freePageList));
  441.     vmStat.numFreePages++;
  442.     }
  443. }
  444.  
  445.  
  446. /*
  447.  * ----------------------------------------------------------------------------
  448.  *
  449.  * TakeOffFreeList --
  450.  *
  451.  *         Take this core map entry off of the free list.
  452.  *
  453.  * Results:
  454.  *         None.
  455.  *
  456.  * Side effects:
  457.  *    Free list modified and core map entry modified.
  458.  * ----------------------------------------------------------------------------
  459.  */
  460. INTERNAL static void
  461. TakeOffFreeList(corePtr)
  462.     VmCore    *corePtr;
  463. {
  464.     VmListRemove((List_Links *) corePtr);
  465.     vmStat.numFreePages--;
  466. }
  467.  
  468.  
  469. /*
  470.  * ----------------------------------------------------------------------------
  471.  *
  472.  * VmPutOnFreePageList --
  473.  *
  474.  *      Put the given page frame onto the free list.
  475.  *
  476.  * Results:
  477.  *         None.
  478.  *
  479.  * Side effects:
  480.  *         None.
  481.  *
  482.  * ----------------------------------------------------------------------------
  483.  */
  484. INTERNAL void
  485. VmPutOnFreePageList(pfNum)
  486.     unsigned    int    pfNum;        /* The page frame to be freed. */
  487. {
  488.     if (pfNum == 0) {
  489.     /*
  490.      * Page frame number 0 is special because a page frame of 0 on a
  491.      * user page fault has special meaning.  Thus if the kernel decides
  492.      * to free page frame 0 then we can't make this page eligible for user
  493.      * use.  Instead of throwing it away put it onto the reserve list
  494.      * because only the kernel uses pages on the reserve list.
  495.      */
  496.     PutOnReserveList(&coreMap[pfNum]);
  497.     } else {
  498.     PutOnFreeList(&coreMap[pfNum]);
  499.     }
  500. }
  501.  
  502.  
  503. /*
  504.  * ----------------------------------------------------------------------------
  505.  *
  506.  * PutOnDirtyList --
  507.  *
  508.  *    Put the given core map entry onto the dirty list and wakeup the page
  509.  *    out daemon.
  510.  *
  511.  * Results:
  512.  *         None.
  513.  *
  514.  * Side effects:
  515.  *         Page added to dirty list, number of dirty pages is incremented and 
  516.  *    number of active page out processes may be incremented.
  517.  *
  518.  * ----------------------------------------------------------------------------
  519.  */
  520. INTERNAL static void
  521. PutOnDirtyList(corePtr)
  522.     register    VmCore    *corePtr;
  523. {
  524.     vmStat.numDirtyPages++;
  525.     VmListInsert((List_Links *) corePtr, LIST_ATREAR(dirtyPageList));
  526.     corePtr->flags |= VM_DIRTY_PAGE;
  527.     if (vmStat.numDirtyPages - numPageOutProcs > 0 &&
  528.     numPageOutProcs < vmMaxPageOutProcs) { 
  529.     Proc_CallFunc(PageOut, (ClientData) numPageOutProcs, 0);
  530.     numPageOutProcs++;
  531.     }
  532. }
  533.  
  534.  
  535. /*
  536.  * ----------------------------------------------------------------------------
  537.  *
  538.  * TakeOffDirtyList --
  539.  *
  540.  *         Take this core map entry off of the dirty list.
  541.  *
  542.  * Results:
  543.  *         None.
  544.  *
  545.  * Side effects:
  546.  *    Dirty list modified and core map entry modified.
  547.  * ----------------------------------------------------------------------------
  548.  */
  549. INTERNAL static void
  550. TakeOffDirtyList(corePtr)
  551.     VmCore    *corePtr;
  552. {
  553.     VmListRemove((List_Links *) corePtr);
  554.     vmStat.numDirtyPages--;
  555. }
  556.  
  557.  
  558. /*
  559.  * ----------------------------------------------------------------------------
  560.  *
  561.  * VmPutOnDirtyList --
  562.  *
  563.  *         Put the given page onto the front of the dirty list.  It is assumed
  564.  *    the page is currently on either the allocate list or the dirty list.
  565.  *    In either case mark the page such that it will not get freed until
  566.  *    it is written out.
  567.  *
  568.  * Results:
  569.  *         None.
  570.  *
  571.  * Side effects:
  572.  *         The dirty list is modified.
  573.  *
  574.  * ----------------------------------------------------------------------------
  575.  */
  576. ENTRY void
  577. VmPutOnDirtyList(pfNum)
  578.     unsigned    int    pfNum;
  579. {
  580.     register    VmCore    *corePtr; 
  581.  
  582.     LOCK_MONITOR;
  583.  
  584.     corePtr = &(coreMap[pfNum]);
  585.     if (!(corePtr->flags & VM_DIRTY_PAGE)) {
  586.     TakeOffAllocList(corePtr);
  587.     PutOnDirtyList(corePtr);
  588.     }
  589.     corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  590.  
  591.     UNLOCK_MONITOR;
  592. }
  593.  
  594.  
  595. /*
  596.  * ----------------------------------------------------------------------------
  597.  *
  598.  *    Routines to validate and invalidate pages.
  599.  *
  600.  * ----------------------------------------------------------------------------
  601.  */
  602.  
  603.  
  604. /*
  605.  * ----------------------------------------------------------------------------
  606.  *
  607.  * VmPageValidate --
  608.  *
  609.  *         Validate the page at the given virtual address.
  610.  *
  611.  * Results:
  612.  *         None.
  613.  *
  614.  * Side effects:
  615.  *    None.
  616.  * ----------------------------------------------------------------------------
  617.  */
  618. ENTRY void
  619. VmPageValidate(virtAddrPtr)
  620.     Vm_VirtAddr    *virtAddrPtr;
  621. {
  622.     LOCK_MONITOR;
  623.  
  624.     VmPageValidateInt(virtAddrPtr, 
  625.               VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page));
  626.  
  627.     UNLOCK_MONITOR;
  628. }
  629.  
  630.  
  631. /*
  632.  * ----------------------------------------------------------------------------
  633.  *
  634.  * VmPageValidateInt --
  635.  *
  636.  *         Validate the page at the given virtual address.
  637.  *
  638.  * Results:
  639.  *         None.
  640.  *
  641.  * Side effects:
  642.  *    Page table modified.
  643.  * ----------------------------------------------------------------------------
  644.  */
  645. INTERNAL void
  646. VmPageValidateInt(virtAddrPtr, ptePtr)
  647.     Vm_VirtAddr        *virtAddrPtr;
  648.     register    Vm_PTE    *ptePtr;
  649. {
  650.     if  (!(*ptePtr & VM_PHYS_RES_BIT)) {
  651.     virtAddrPtr->segPtr->resPages++;
  652.     *ptePtr |= VM_PHYS_RES_BIT;
  653.     }
  654.     VmMach_PageValidate(virtAddrPtr, *ptePtr);
  655. }
  656.  
  657.  
  658. /*
  659.  * ----------------------------------------------------------------------------
  660.  *
  661.  * VmPageInvalidate --
  662.  *
  663.  *         Invalidate the page at the given virtual address.
  664.  *
  665.  * Results:
  666.  *         None.
  667.  *
  668.  * Side effects:
  669.  *    None.
  670.  * ----------------------------------------------------------------------------
  671.  */
  672. ENTRY void
  673. VmPageInvalidate(virtAddrPtr)
  674.     register    Vm_VirtAddr    *virtAddrPtr;
  675. {
  676.     LOCK_MONITOR;
  677.  
  678.     VmPageInvalidateInt(virtAddrPtr, 
  679.     VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page));
  680.  
  681.     UNLOCK_MONITOR;
  682. }
  683.  
  684.  
  685. /*
  686.  * ----------------------------------------------------------------------------
  687.  *
  688.  * VmPageInvalidateInt --
  689.  *
  690.  *         Invalidate the page at the given virtual address.
  691.  *
  692.  * Results:
  693.  *         None.
  694.  *
  695.  * Side effects:
  696.  *    Page table modified.
  697.  * ----------------------------------------------------------------------------
  698.  */
  699. INTERNAL void
  700. VmPageInvalidateInt(virtAddrPtr, ptePtr)
  701.     Vm_VirtAddr        *virtAddrPtr;
  702.     register    Vm_PTE    *ptePtr;
  703. {
  704.     if (*ptePtr & VM_PHYS_RES_BIT) {
  705.     virtAddrPtr->segPtr->resPages--;
  706.     VmMach_PageInvalidate(virtAddrPtr, Vm_GetPageFrame(*ptePtr), FALSE);
  707.     *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  708.     }
  709. }
  710.  
  711.  
  712. /*
  713.  * ----------------------------------------------------------------------------
  714.  *
  715.  *    Routines to lock and unlock pages.
  716.  *
  717.  * ----------------------------------------------------------------------------
  718.  */
  719.  
  720. /*
  721.  * ----------------------------------------------------------------------------
  722.  *
  723.  * VmLockPageInt --
  724.  *
  725.  *         Increment the lock count on a page.
  726.  *
  727.  * Results:
  728.  *         None.
  729.  *
  730.  * Side effects:
  731.  *    The core map entry for the page has its lock count incremented.
  732.  *
  733.  * ----------------------------------------------------------------------------
  734.  */
  735. INTERNAL void
  736. VmLockPageInt(pfNum)
  737.     unsigned    int        pfNum;
  738. {
  739.     coreMap[pfNum].lockCount++;
  740. }
  741.  
  742.  
  743. /*
  744.  * ----------------------------------------------------------------------------
  745.  *
  746.  * VmUnlockPage --
  747.  *
  748.  *         Decrement the lock count on a page.
  749.  *
  750.  * Results:
  751.  *         None.
  752.  *
  753.  * Side effects:
  754.  *    The core map entry for the page has its lock count decremented.
  755.  *
  756.  * ----------------------------------------------------------------------------
  757.  */
  758. ENTRY void
  759. VmUnlockPage(pfNum)
  760.     unsigned    int    pfNum;
  761. {
  762.     LOCK_MONITOR;
  763.     coreMap[pfNum].lockCount--;
  764.     if (coreMap[pfNum].lockCount < 0) {
  765.     panic("VmUnlockPage: Coremap lock count < 0\n");
  766.     }
  767.     UNLOCK_MONITOR;
  768. }
  769.  
  770.  
  771. /*
  772.  * ----------------------------------------------------------------------------
  773.  *
  774.  * VmUnlockPageInt --
  775.  *
  776.  *         Decrement the lock count on a page.
  777.  *
  778.  * Results:
  779.  *         None.
  780.  *
  781.  * Side effects:
  782.  *    The core map entry for the page has its lock count decremented.
  783.  *
  784.  * ----------------------------------------------------------------------------
  785.  */
  786. INTERNAL void
  787. VmUnlockPageInt(pfNum)
  788.     unsigned    int    pfNum;
  789. {
  790.     coreMap[pfNum].lockCount--;
  791.     if (coreMap[pfNum].lockCount < 0) {
  792.     panic("VmUnlockPage: Coremap lock count < 0\n");
  793.     }
  794. }
  795.  
  796.  
  797. /*
  798.  * ----------------------------------------------------------------------------
  799.  *
  800.  * VmPageSwitch --
  801.  *
  802.  *         Move the given page from the current owner to the new owner.
  803.  *
  804.  * Results:
  805.  *         None.
  806.  *
  807.  * Side effects:
  808.  *    The segment pointer int the core map entry for the page is modified and
  809.  *    the page is unlocked.
  810.  *
  811.  * ----------------------------------------------------------------------------
  812.  */
  813. INTERNAL void
  814. VmPageSwitch(pageNum, newSegPtr)
  815.     unsigned    int    pageNum;
  816.     Vm_Segment        *newSegPtr;
  817. {
  818.     coreMap[pageNum].virtPage.segPtr = newSegPtr;
  819.     coreMap[pageNum].lockCount--;
  820. }
  821.  
  822.  
  823. /*
  824.  * ----------------------------------------------------------------------------
  825.  *
  826.  *    Routines to get reference times of VM pages.
  827.  *
  828.  * ----------------------------------------------------------------------------
  829.  */
  830.  
  831. /*
  832.  * ----------------------------------------------------------------------------
  833.  *
  834.  * Vm_GetRefTime --
  835.  *
  836.  *         Return the age of the LRU page (0 if is a free page).
  837.  *
  838.  * Results:
  839.  *         Age of LRU page (0 if there is a free page).
  840.  *
  841.  * Side effects:
  842.  *         None.
  843.  *
  844.  * ----------------------------------------------------------------------------
  845.  */
  846. ENTRY int
  847. Vm_GetRefTime()
  848. {
  849.     register    VmCore    *corePtr; 
  850.     int            refTime;
  851.  
  852.     LOCK_MONITOR;
  853.  
  854.     vmStat.fsAsked++;
  855.  
  856.     if (swapDown || (vmStat.numFreePages + vmStat.numUserPages + 
  857.              vmStat.numDirtyPages <= vmStat.minVMPages)) {
  858.     /*
  859.      * We are already at or below the minimum amount of memory that
  860.      * we are guaranteed for our use so refuse to give any memory to
  861.      * the file system.
  862.      */
  863.     UNLOCK_MONITOR;
  864.     return((int) 0x7fffffff);
  865.     }
  866.  
  867.     if (!List_IsEmpty(freePageList)) {
  868.     vmStat.haveFreePage++;
  869.     refTime = 0;
  870.     if (vmDebug) {
  871.         printf("Vm_GetRefTime: VM has free page\n");
  872.     }
  873.     } else {
  874.     refTime = (int) 0x7fffffff;
  875.     if (!List_IsEmpty(dirtyPageList)) {
  876.         corePtr = (VmCore *) List_First(dirtyPageList);
  877.         refTime = corePtr->lastRef;
  878.     }
  879.     if (!List_IsEmpty(allocPageList)) {
  880.         corePtr = (VmCore *) List_First(allocPageList);
  881.         if (corePtr->lastRef < refTime) {
  882.         refTime = corePtr->lastRef;
  883.         }
  884.     }
  885.     if (vmDebug) {
  886.         printf("Vm_GetRefTime: Reftime = %d\n", refTime);
  887.     }
  888.     }
  889.  
  890.     if (vmAlwaysRefuse) {
  891.     refTime = INT_MAX;
  892.     } else if (vmAlwaysSayYes) {
  893.     refTime = 0;
  894.     } else {
  895.     refTime += vmCurPenalty;
  896.     }
  897.  
  898.     UNLOCK_MONITOR;
  899.  
  900.     return(refTime);
  901. }
  902.  
  903. /*
  904.  * ----------------------------------------------------------------------------
  905.  *
  906.  * GetRefTime --
  907.  *
  908.  *         Return either the first free page on the allocate list or the
  909.  *    last reference time of the first page on the list.
  910.  *
  911.  * Results:
  912.  *         None.
  913.  *
  914.  * Side effects:
  915.  *         First page removed from allocate list if one is free.
  916.  *
  917.  * ----------------------------------------------------------------------------
  918.  */
  919. ENTRY static void
  920. GetRefTime(refTimePtr, pagePtr)
  921.     register    int    *refTimePtr;
  922.     unsigned    int    *pagePtr;
  923. {
  924.     register    VmCore    *corePtr; 
  925.  
  926.     LOCK_MONITOR;
  927.  
  928.     if (!List_IsEmpty(freePageList)) {
  929.     vmStat.gotFreePage++;
  930.     corePtr = (VmCore *) List_First(freePageList);
  931.     TakeOffFreeList(corePtr);
  932.     *pagePtr = corePtr - coreMap;
  933.     } else {
  934.     *refTimePtr = (int) 0x7fffffff;
  935.     if (!List_IsEmpty(dirtyPageList)) {
  936.         corePtr = (VmCore *) List_First(dirtyPageList);
  937.         *refTimePtr = corePtr->lastRef;
  938.     }
  939.     if (!List_IsEmpty(allocPageList)) {
  940.         corePtr = (VmCore *) List_First(allocPageList);
  941.         if (corePtr->lastRef < *refTimePtr) {
  942.         *refTimePtr = corePtr->lastRef;
  943.         }
  944.     }
  945.     *pagePtr = VM_NO_MEM_VAL;
  946.     }
  947.  
  948.     UNLOCK_MONITOR;
  949. }
  950.  
  951. /*
  952.  * ----------------------------------------------------------------------------
  953.  *
  954.  *    Routines to allocate pages.
  955.  *
  956.  * ----------------------------------------------------------------------------
  957.  */
  958.  
  959. /*
  960.  * ----------------------------------------------------------------------------
  961.  *
  962.  * DoPageAllocate --
  963.  *
  964.  *         Grab the monitor lock and call VmPageAllocate.
  965.  *
  966.  * Results:
  967.  *         The virtual page number that is allocated.
  968.  *
  969.  * Side effects:
  970.  *         None.
  971.  *
  972.  * ----------------------------------------------------------------------------
  973.  */
  974. ENTRY static unsigned    int
  975. DoPageAllocate(virtAddrPtr, flags)
  976.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that 
  977.                    indicates the segment and virtual page 
  978.                    that this physical page is being allocated 
  979.                    for */
  980.     int        flags;        /* VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY */
  981. {
  982.     unsigned    int page;
  983.  
  984.     LOCK_MONITOR;
  985.  
  986.     while (swapDown) {
  987.     (void)Sync_Wait(&swapDownCondition, FALSE);
  988.     }
  989.     page = VmPageAllocateInt(virtAddrPtr, flags);
  990.  
  991.     UNLOCK_MONITOR;
  992.     return(page);
  993. }
  994.  
  995.  
  996. /*
  997.  * ----------------------------------------------------------------------------
  998.  *
  999.  * VmPageAllocate --
  1000.  *
  1001.  *         Return a page frame.  Will either get a page from VM or FS depending
  1002.  *    on the LRU comparison and if there is a free page or not.
  1003.  *
  1004.  * Results:
  1005.  *         The page frame number that is allocated.
  1006.  *
  1007.  * Side effects:
  1008.  *         None.
  1009.  *
  1010.  * ----------------------------------------------------------------------------
  1011.  */
  1012. unsigned int
  1013. VmPageAllocate(virtAddrPtr, flags)
  1014.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that this
  1015.                  * page frame is being allocated for */
  1016.     int        flags;        /* VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY. */
  1017. {
  1018.     unsigned    int    page;
  1019.     int            refTime;
  1020.     int            tPage;
  1021.  
  1022.     vmStat.numAllocs++;
  1023.  
  1024.     GetRefTime(&refTime, &page);
  1025.     if (page == VM_NO_MEM_VAL) {
  1026.     Fscache_GetPageFromFS(refTime + vmCurPenalty, &tPage);
  1027.     if (tPage == -1) {
  1028.         vmStat.pageAllocs++;
  1029.         return(DoPageAllocate(virtAddrPtr, flags));
  1030.     } else {
  1031.         page = tPage;
  1032.         vmStat.gotPageFromFS++;
  1033.         if (vmDebug) {
  1034.         printf("VmPageAllocate: Took page from FS (refTime = %d)\n",
  1035.                 refTime);
  1036.         }
  1037.     }
  1038.     }
  1039.  
  1040.     /*
  1041.      * Move the page to the end of the allocate list and initialize the core 
  1042.      * map entry.
  1043.      */
  1044.     PutOnAllocList(virtAddrPtr, page);
  1045.  
  1046.     return(page);
  1047. }
  1048.  
  1049.  
  1050. /*
  1051.  * ----------------------------------------------------------------------------
  1052.  *
  1053.  * VmPageAllocateInt --
  1054.  *
  1055.  *         This routine will return the page frame number of the first free or
  1056.  *         unreferenced, unmodified, unlocked page that it can find on the 
  1057.  *    allocate list.  The core map entry for this page will be initialized to 
  1058.  *    contain the virtual page number and the lock count will be set to 
  1059.  *    1 to indicate that this page is locked down.
  1060.  *
  1061.  *    This routine will sleep if the entire allocate list is dirty in order
  1062.  *    to give the page-out daemon some time to clean pages.
  1063.  *
  1064.  * Results:
  1065.  *         The physical page number that is allocated.
  1066.  *
  1067.  * Side effects:
  1068.  *         The allocate list is modified and the  dirty list may be modified.
  1069.  *    In addition the appropriate core map entry is initialized.
  1070.  *
  1071.  * ----------------------------------------------------------------------------
  1072.  */
  1073. INTERNAL unsigned int
  1074. VmPageAllocateInt(virtAddrPtr, flags)
  1075.     Vm_VirtAddr    *virtAddrPtr;    /* The translated virtual address that 
  1076.                    this page frame is being allocated for */
  1077.     int        flags;        /* VM_CAN_BLOCK if can block if non memory is
  1078.                  * available. VM_ABORT_WHEN_DIRTY if should
  1079.                  * abort even if VM_CAN_BLOCK is set if have
  1080.                  * exceeded the maximum number of dirty pages
  1081.                  * on the dirty list. */
  1082. {
  1083.     register    VmCore    *corePtr; 
  1084.     register    Vm_PTE    *ptePtr;
  1085.     Time        curTime;
  1086.     List_Links        endMarker;
  1087.     Boolean        referenced;
  1088.     Boolean        modified;
  1089.  
  1090.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  1091.  
  1092.     vmStat.numListSearches++;
  1093.  
  1094. again:
  1095.     if (!List_IsEmpty(freePageList)) {
  1096.     corePtr = (VmCore *) List_First(freePageList);
  1097.     TakeOffFreeList(corePtr);
  1098.     vmStat.usedFreePage++;
  1099.     } else {
  1100.     /*
  1101.      * Put a marker at the end of the core map so that we can detect loops.
  1102.      */
  1103.     endMarker.nextPtr = (List_Links *) NIL;
  1104.     endMarker.prevPtr = (List_Links *) NIL;
  1105.     VmListInsert(&endMarker, LIST_ATREAR(allocPageList));
  1106.  
  1107.     /*
  1108.      * Loop examining the page on the front of the allocate list until 
  1109.      * a free or unreferenced, unmodified, unlocked page frame is found.
  1110.      * If the whole list is examined and nothing found, then return 
  1111.      * VM_NO_MEM_VAL.
  1112.      */
  1113.     while (TRUE) {
  1114.         corePtr = (VmCore *) List_First(allocPageList);
  1115.  
  1116.         /*
  1117.          * See if have gone all of the way through the list without finding
  1118.          * anything.
  1119.          */
  1120.         if (((flags & (VM_CAN_BLOCK | VM_ABORT_WHEN_DIRTY)) && 
  1121.              vmStat.numDirtyPages > vmMaxDirtyPages) ||
  1122.             corePtr == (VmCore *) &endMarker) {    
  1123.         VmListRemove((List_Links *) &endMarker);
  1124.         if (!(flags & VM_CAN_BLOCK)) {
  1125.             return(VM_NO_MEM_VAL);
  1126.         } else {
  1127.             /*
  1128.              * There were no pages available.  Wait for a clean
  1129.              * page to appear on the allocate list.
  1130.              */
  1131.             (void)Sync_Wait(&cleanCondition, FALSE);
  1132.             goto again;
  1133.         }
  1134.         }
  1135.     
  1136.         /*
  1137.          * Make sure that the page is not locked down.
  1138.          */
  1139.         if (corePtr->lockCount > 0) {
  1140.         vmStat.lockSearched++;
  1141.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  1142.         continue;
  1143.         }
  1144.  
  1145.         ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage,
  1146.                  corePtr->virtPage.page);
  1147.         referenced = *ptePtr & VM_REFERENCED_BIT;
  1148.         modified = *ptePtr & VM_MODIFIED_BIT;
  1149.         VmMach_AllocCheck(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  1150.                   &referenced, &modified);
  1151.         /*
  1152.          * Now make sure that the page has not been referenced.  It it has
  1153.          * then clear the reference bit and put it onto the end of the
  1154.          * allocate list.
  1155.          */
  1156.         if (referenced) {
  1157.         vmStat.refSearched++;
  1158.         corePtr->lastRef = curTime.seconds;
  1159.         *ptePtr &= ~VM_REFERENCED_BIT;
  1160.         VmMach_ClearRefBit(&corePtr->virtPage,
  1161.                    Vm_GetPageFrame(*ptePtr));
  1162.         if (vmWriteableRefPageout &&
  1163.             corePtr->virtPage.segPtr->type != VM_CODE) {
  1164.             *ptePtr |= VM_MODIFIED_BIT;
  1165.         }
  1166.  
  1167.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  1168.         /*
  1169.          * Set the last page marker so that we will try to examine this
  1170.          * page again if we go all the way around without finding 
  1171.          * anything.  
  1172.          *
  1173.          * NOTE: This is only a uni-processor solution since
  1174.          *       on a multi-processor a process could be continually 
  1175.          *       touching pages while we are scanning the list.
  1176.          */
  1177.         VmListMove(&endMarker, LIST_ATREAR(allocPageList));
  1178.         continue;
  1179.         }
  1180.  
  1181.         if (corePtr->virtPage.segPtr->type != VM_CODE) {
  1182.         vmStat.potModPages++;
  1183.         }
  1184.         /*
  1185.          * The page is available and it has not been referenced.  Now
  1186.          * it must be determined if it is dirty.
  1187.          */
  1188.         if (modified) {
  1189.         /*
  1190.          * Dirty pages go onto the dirty list.
  1191.          */
  1192.         vmStat.dirtySearched++;
  1193.         TakeOffAllocList(corePtr);
  1194.         PutOnDirtyList(corePtr);
  1195.         continue;
  1196.         }
  1197.  
  1198.         if (corePtr->virtPage.segPtr->type != VM_CODE) {
  1199.         vmStat.notModPages++;
  1200.         }
  1201.         /*
  1202.          * We can take this page.  Invalidate the page for the old segment.
  1203.          * VmMach_AllocCheck will have already invalidated the page for
  1204.          * us in hardware.
  1205.          */
  1206.         corePtr->virtPage.segPtr->resPages--;
  1207.         *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  1208.  
  1209.         TakeOffAllocList(corePtr);
  1210.         VmListRemove(&endMarker);
  1211.         break;
  1212.     }
  1213.     }
  1214.  
  1215.     /*
  1216.      * If this page is being allocated for the kernel segment then don't put
  1217.      * it back onto the allocate list because kernel pages don't exist on
  1218.      * the allocate list.  Otherwise move it to the rear of the allocate list.
  1219.      */
  1220.     if (virtAddrPtr->segPtr != vm_SysSegPtr) {
  1221.     PutOnAllocListRear(corePtr);
  1222.     }
  1223.     corePtr->virtPage = *virtAddrPtr;
  1224.     corePtr->flags = 0;
  1225.     corePtr->lockCount = 1;
  1226.     corePtr->wireCount = 0;
  1227.     corePtr->lastRef = curTime.seconds;
  1228.  
  1229.     return(corePtr - coreMap);
  1230. }
  1231.  
  1232. /*
  1233.  * ----------------------------------------------------------------------------
  1234.  *
  1235.  * VmPageFreeInt --
  1236.  *
  1237.  *      This routine will put the given page frame onto the front of the
  1238.  *      free list if it is not on the dirty list.  If the page frame is on 
  1239.  *    the dirty list then this routine will sleep until the page has been
  1240.  *    cleaned.  The page-out daemon will put the page onto the front of the
  1241.  *    allocate list when it finishes cleaning the page.
  1242.  *
  1243.  * Results:
  1244.  *         None.
  1245.  *
  1246.  * Side effects:
  1247.  *         The free list is modified and the core map entry is set to free
  1248.  *    with a lockcount of 0.
  1249.  *
  1250.  * ----------------------------------------------------------------------------
  1251.  */
  1252. INTERNAL void
  1253. VmPageFreeInt(pfNum)
  1254.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1255. {
  1256.     register    VmCore    *corePtr; 
  1257.  
  1258.     corePtr = &(coreMap[pfNum]);
  1259.  
  1260.     corePtr->flags |= VM_FREE_PAGE;
  1261.     corePtr->lockCount = 0;
  1262.  
  1263.     if (corePtr->virtPage.segPtr == vm_SysSegPtr) {
  1264.         /*
  1265.      * Pages given to the kernel are removed from the allocate list when
  1266.      * they are allocated.  Therefore just put it back onto the free list.
  1267.      */
  1268.     if (corePtr->flags & (VM_DIRTY_PAGE | VM_PAGE_BEING_CLEANED)) {
  1269.         panic("VmPageFreeInt: Kernel page on dirty list\n");
  1270.     }
  1271.     PutOnFreeList(corePtr);
  1272.     } else {
  1273.     /*
  1274.      * If the page is being written then wait for it to finish.
  1275.      * Once it has been cleaned it will automatically be put onto the free
  1276.      * list.  We must wait for it to be cleaned because 
  1277.      * the segment may die otherwise while the page is still waiting to be 
  1278.      * cleaned.  This would be a disaster because the page-out daemon uses
  1279.      * the segment table entry to determine where to write the page.
  1280.      */
  1281.     if (corePtr->flags & 
  1282.             (VM_PAGE_BEING_CLEANED | VM_DONT_FREE_UNTIL_CLEAN)) {
  1283.         do {
  1284.         corePtr->flags |= VM_SEG_PAGEOUT_WAIT;
  1285.         vmStat.cleanWait++;
  1286.         (void) Sync_Wait(&corePtr->virtPage.segPtr->condition, FALSE);
  1287.         } while (corePtr->flags & 
  1288.             (VM_PAGE_BEING_CLEANED | VM_DONT_FREE_UNTIL_CLEAN));
  1289.     } else {
  1290.         if (corePtr->flags & VM_DIRTY_PAGE) {
  1291.         TakeOffDirtyList(corePtr);
  1292.         } else {
  1293.         TakeOffAllocList(corePtr);
  1294.         }
  1295.         PutOnFreeList(corePtr);
  1296.     }
  1297.     }
  1298. }
  1299.  
  1300.  
  1301. /*
  1302.  * ----------------------------------------------------------------------------
  1303.  *
  1304.  * VmPageFree --
  1305.  *
  1306.  *      Free the given page.  Call VmPageFreeInt to do the work.
  1307.  *
  1308.  * Results:
  1309.  *         None.
  1310.  *
  1311.  * Side effects:
  1312.  *         None.
  1313.  *
  1314.  * ----------------------------------------------------------------------------
  1315.  */
  1316. ENTRY void
  1317. VmPageFree(pfNum)
  1318.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1319. {
  1320.     LOCK_MONITOR;
  1321.  
  1322.     VmPageFreeInt(pfNum);
  1323.  
  1324.     UNLOCK_MONITOR;
  1325. }
  1326.  
  1327.  
  1328. /*
  1329.  * ----------------------------------------------------------------------------
  1330.  *
  1331.  * Vm_ReservePage --
  1332.  *
  1333.  *      Take a page out of the available pages because this page is
  1334.  *    being used by the hardware dependent module.  This routine is
  1335.  *    called at boot time.
  1336.  *
  1337.  * Results:
  1338.  *         None.
  1339.  *
  1340.  * Side effects:
  1341.  *         None.
  1342.  *
  1343.  * ----------------------------------------------------------------------------
  1344.  */
  1345. ENTRY void
  1346. Vm_ReservePage(pfNum)
  1347.     unsigned    int    pfNum;        /* The page frame to be freed. */
  1348. {
  1349.     register    VmCore    *corePtr;
  1350.  
  1351.     LOCK_MONITOR;
  1352.  
  1353.     if (pfNum < vmStat.numPhysPages) {
  1354.     corePtr = &coreMap[pfNum];
  1355.     TakeOffFreeList(corePtr);
  1356.     corePtr->virtPage.segPtr = vm_SysSegPtr;
  1357.     corePtr->flags = 0;
  1358.     corePtr->lockCount = 1;
  1359.     }
  1360.  
  1361.     UNLOCK_MONITOR;
  1362. }
  1363.         
  1364.  
  1365. /*-----------------------------------------------------------------------
  1366.  *
  1367.  *         Routines to handle page faults.
  1368.  *
  1369.  * Page fault handling is divided into three routines.  The first
  1370.  * routine is Vm_PageIn.  It calls two monitored routines PreparePage and
  1371.  * FinishPage to do most of the monitor level work.
  1372.  */
  1373.  
  1374. typedef enum {
  1375.     IS_COR,    /* This page is copy-on-reference. */
  1376.     IS_COW,     /* This page is copy-on-write. */
  1377.     IS_DONE,     /* The page-in has already completed. */
  1378.     NOT_DONE,    /* The page-in is not yet done yet. */
  1379. } PrepareResult;
  1380.  
  1381. static PrepareResult PreparePage _ARGS_((register Vm_VirtAddr *virtAddrPtr, Boolean protFault, register Vm_PTE *curPTEPtr));
  1382. static void FinishPage _ARGS_((register Vm_VirtAddr *transVirtAddrPtr, register Vm_PTE *ptePtr));
  1383.  
  1384.  
  1385. /*
  1386.  * ----------------------------------------------------------------------------
  1387.  *
  1388.  * Vm_PageIn --
  1389.  *
  1390.  *     This routine is called to read in the page at the given virtual address.
  1391.  *
  1392.  * Results:
  1393.  *     SUCCESS if the page-in was successful and FAILURE otherwise.
  1394.  *
  1395.  * Side effects:
  1396.  *     None.
  1397.  *
  1398.  * ----------------------------------------------------------------------------
  1399.  */
  1400. ReturnStatus
  1401. Vm_PageIn(virtAddr, protFault)
  1402.     Address     virtAddr;    /* The virtual address of the desired page */
  1403.     Boolean    protFault;    /* TRUE if fault is because of a protection
  1404.                  * violation. */
  1405. {
  1406.     register    Vm_PTE         *ptePtr;
  1407.     register    Vm_Segment    *segPtr;
  1408.     register    int        page;
  1409.     Vm_VirtAddr             transVirtAddr;    
  1410.     ReturnStatus         status;
  1411.     Proc_ControlBlock        *procPtr;
  1412.     unsigned    int        virtFrameNum;
  1413.     PrepareResult        result;
  1414.  
  1415.     vmStat.totalFaults++;
  1416.  
  1417.     procPtr = Proc_GetCurrentProc();
  1418.     /*
  1419.      * Determine which segment this virtual address falls into.
  1420.      */
  1421.     VmVirtAddrParse(procPtr, virtAddr, &transVirtAddr);
  1422.     segPtr = transVirtAddr.segPtr;
  1423.     page = transVirtAddr.page;
  1424.     if (segPtr == (Vm_Segment *) NIL) {
  1425.     return(FAILURE);
  1426.     }
  1427.     if (segPtr->flags & VM_SEG_IO_ERROR) {
  1428.     /*
  1429.      * Bad segment - disk full..  Go to pageinDone to clean up ptUserCount.
  1430.      * If a process is wildly growing its stack we'll have the heap locked
  1431.      * while we try to grow the stack, and we have to unlock the heap.
  1432.      */
  1433.     if (segPtr->type == VM_SHARED) {
  1434.         printf("Vm_PageIn: io error\n");
  1435.     }
  1436.     status = FAILURE;
  1437.     goto pageinDone;
  1438.     }
  1439.     if (protFault && ( segPtr->type == VM_CODE ||
  1440.         (transVirtAddr.flags & VM_READONLY_SEG))) {
  1441.     /*
  1442.      * Access violation.  Go to pageinDone to clean up ptUserCount.
  1443.      */
  1444.     if (segPtr->type == VM_SHARED) {
  1445.         printf("Vm_PageIn: access violation\n");
  1446.     }
  1447.     status = FAILURE;
  1448.     goto pageinDone;
  1449.     }
  1450.  
  1451.     /*
  1452.      * Make sure that the virtual address is within the allocated part of the
  1453.      * segment.  If not, then either return error if heap or code segment,
  1454.      * or automatically expand the stack if stack segment.
  1455.      */
  1456.     if (!VmCheckBounds(&transVirtAddr)) {
  1457.     if (segPtr->type == VM_STACK) {
  1458.         int    lastPage;
  1459.         /*
  1460.          * If this is a stack segment, then automatically grow it.
  1461.          */
  1462.         lastPage = mach_LastUserStackPage - segPtr->numPages;
  1463.         status = VmAddToSeg(segPtr, page, lastPage);
  1464.         if (status != SUCCESS) {
  1465.         goto pageinDone;
  1466.         }
  1467.     } else {
  1468.         if (segPtr->type == VM_SHARED) {
  1469.         dprintf("Vm_PageIn: VmCheckBounds failure\n");
  1470.         }
  1471.         status = FAILURE;
  1472.         goto pageinDone;
  1473.     }
  1474.     }
  1475.  
  1476.     switch (segPtr->type) {
  1477.     case VM_CODE:
  1478.         vmStat.codeFaults++;
  1479.         break;
  1480.     case VM_HEAP:
  1481.         vmStat.heapFaults++;
  1482.         break;
  1483.     case VM_STACK:
  1484.         vmStat.stackFaults++;
  1485.         break;
  1486.     }
  1487.  
  1488.     ptePtr = VmGetAddrPTEPtr(&transVirtAddr, page);
  1489.  
  1490.     if (protFault && (*ptePtr & VM_READ_ONLY_PROT) &&
  1491.         !(*ptePtr & VM_COR_CHECK_BIT)) {
  1492.     status = FAILURE;
  1493.     if (segPtr->type == VM_SHARED) {
  1494.         printf("Vm_PageIn: permission failure\n");
  1495.     }
  1496.     goto pageinDone;
  1497.     }
  1498.  
  1499.     /*
  1500.      * Fetch the next page.
  1501.      */
  1502.     if (vmPrefetch) {
  1503.     VmPrefetch(&transVirtAddr, ptePtr + 1);
  1504.     }
  1505.  
  1506.     while (TRUE) {
  1507.     /*
  1508.      * Do the first part of the page-in.
  1509.      */
  1510.     result = PreparePage(&transVirtAddr, protFault, ptePtr);
  1511.     if (!vm_CanCOW && (result == IS_COR || result == IS_COW)) {
  1512.         panic("Vm_PageIn: Bogus COW or COR\n");
  1513.     }
  1514.     if (result == IS_COR) {
  1515.         status = VmCOR(&transVirtAddr);
  1516.         if (status != SUCCESS) {
  1517.         if (segPtr->type == VM_SHARED) {
  1518.             printf("Vm_PageIn: VmCOR failure\n");
  1519.         }
  1520.         status = FAILURE;
  1521.         goto pageinError;
  1522.         }
  1523.     } else if (result == IS_COW) {
  1524.         VmCOW(&transVirtAddr);
  1525.     } else {
  1526.         break;
  1527.     }
  1528.     }
  1529.     if (result == IS_DONE) {
  1530.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1531.         dprintf("shared page at %x done early\n", virtAddr);
  1532.     }
  1533.     status = SUCCESS;
  1534.     goto pageinDone;
  1535.     }
  1536.  
  1537.     /*
  1538.      * Allocate a page.
  1539.      */
  1540.     virtFrameNum = VmPageAllocate(&transVirtAddr, TRUE);
  1541.     *ptePtr |= virtFrameNum;
  1542.  
  1543.     if (transVirtAddr.segPtr->type == VM_SHARED && debugVmStubs) {
  1544.     printf("paging in shared page to %x\n", virtAddr);
  1545.     }
  1546.  
  1547.     /*
  1548.      * Call the appropriate routine to fill the page.
  1549.      */
  1550.     if (*ptePtr & VM_ZERO_FILL_BIT) {
  1551.     vmStat.zeroFilled++;
  1552.     VmZeroPage(virtFrameNum);
  1553.     *ptePtr |= VM_MODIFIED_BIT;
  1554.     status = SUCCESS;
  1555.     if (vm_Tracing) {
  1556.         Vm_TracePageFault    faultRec;
  1557.  
  1558.         faultRec.segNum = transVirtAddr.segPtr->segNum;
  1559.         faultRec.pageNum = transVirtAddr.page;
  1560.         faultRec.faultType = VM_TRACE_ZERO_FILL;
  1561.         VmStoreTraceRec(VM_TRACE_PAGE_FAULT_REC, sizeof(faultRec),
  1562.                 (Address)&faultRec, TRUE);
  1563.     }
  1564.     } else if (*ptePtr & VM_ON_SWAP_BIT) {
  1565.     vmStat.psFilled++;
  1566.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1567.         dprintf("Vm_PageIn: paging in shared page %d\n",transVirtAddr.page);
  1568.     }
  1569.     status = VmPageServerRead(&transVirtAddr, virtFrameNum);
  1570.     if (vm_Tracing) {
  1571.         Vm_TracePageFault    faultRec;
  1572.  
  1573.         faultRec.segNum = transVirtAddr.segPtr->segNum;
  1574.         faultRec.pageNum = transVirtAddr.page;
  1575.         faultRec.faultType = VM_TRACE_SWAP_FILE;
  1576.         VmStoreTraceRec(VM_TRACE_PAGE_FAULT_REC, sizeof(faultRec),
  1577.                 (Address)&faultRec, TRUE);
  1578.     }
  1579.     } else {
  1580.     vmStat.fsFilled++;
  1581.     status = VmFileServerRead(&transVirtAddr, virtFrameNum);
  1582.     if (vm_Tracing && transVirtAddr.segPtr->type == VM_HEAP) {
  1583.         Vm_TracePageFault    faultRec;
  1584.  
  1585.         faultRec.segNum = transVirtAddr.segPtr->segNum;
  1586.         faultRec.pageNum = transVirtAddr.page;
  1587.         faultRec.faultType = VM_TRACE_OBJ_FILE;
  1588.         VmStoreTraceRec(VM_TRACE_PAGE_FAULT_REC, sizeof(faultRec),
  1589.                 (Address)&faultRec, TRUE);
  1590.     }
  1591.     }
  1592.  
  1593.     *ptePtr |= VM_REFERENCED_BIT;
  1594.     if (vmWriteablePageout && transVirtAddr.segPtr->type != VM_CODE) {
  1595.     *ptePtr |= VM_MODIFIED_BIT;
  1596.     }
  1597.  
  1598.     /*
  1599.      * Finish up the page-in process.
  1600.      */
  1601.     FinishPage(&transVirtAddr, ptePtr);
  1602.  
  1603.     /*
  1604.      * Now check to see if the read succeeded.  If not destroy all processes
  1605.      * that are sharing the code segment.
  1606.      */
  1607. pageinError:
  1608.     if (status != SUCCESS) {
  1609.     if (transVirtAddr.segPtr->type == VM_SHARED) {
  1610.         printf("Vm_PageIn: Page read failed.  Invalidating pages.\n");
  1611.         VmPageFree(Vm_GetPageFrame(*ptePtr));
  1612.         VmPageInvalidateInt(&transVirtAddr, ptePtr);
  1613.     } else {
  1614.         VmKillSharers(segPtr);
  1615.     }
  1616.     }
  1617.  
  1618. pageinDone:
  1619.  
  1620.     if (transVirtAddr.flags & VM_HEAP_PT_IN_USE) {
  1621.     /*
  1622.      * The heap segment has been made not expandable by VmVirtAddrParse
  1623.      * so that the address parse would remain valid.  Decrement the
  1624.      * in use count now.
  1625.      */
  1626.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  1627.     }
  1628.  
  1629.     return(status);
  1630. }
  1631.  
  1632.  
  1633. /*
  1634.  * ----------------------------------------------------------------------------
  1635.  *
  1636.  * PreparePage --
  1637.  *
  1638.  *    This routine performs the first half of the page-in process.
  1639.  *    It will return a status to the caller telling them what the status
  1640.  *    of the page is.
  1641.  *
  1642.  * Results:
  1643.  *    IS_DONE if the page is already resident in memory and it is not a 
  1644.  *    COW faults.  IS_COR is it is for a copy-on-reference fault.  IS_COW
  1645.  *    if is for a copy-on-write fault.  Otherwise returns NOT_DONE.
  1646.  *
  1647.  * Side effects:
  1648.  *    *ptePtrPtr is set to point to the page table entry for this virtual
  1649.  *    page.  In progress bit set if the NOT_DONE status is returned.
  1650.  *
  1651.  * ----------------------------------------------------------------------------
  1652.  */
  1653. ENTRY static PrepareResult
  1654. PreparePage(virtAddrPtr, protFault, curPTEPtr)
  1655.     register Vm_VirtAddr *virtAddrPtr;     /* The translated virtual address */
  1656.     Boolean        protFault;    /* TRUE if faulted because of a
  1657.                      * protection fault. */
  1658.     register    Vm_PTE    *curPTEPtr;    /* Page table pointer for the page. */
  1659. {
  1660.     PrepareResult    retVal;
  1661.  
  1662.     LOCK_MONITOR;
  1663.  
  1664. again:
  1665.     if (*curPTEPtr & VM_IN_PROGRESS_BIT) {
  1666.     /*
  1667.      * The page is being faulted on by someone else.  In this case wait
  1668.      * for the page fault to complete.
  1669.      */
  1670.     vmStat.collFaults++;
  1671.     (void) Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  1672.     goto again;
  1673.     } else if (*curPTEPtr & VM_COR_BIT) {
  1674.     /*
  1675.      * Copy-on-reference fault.
  1676.      */
  1677.     retVal = IS_COR;
  1678.     } else if (protFault && (*curPTEPtr & VM_COW_BIT) && 
  1679.            (*curPTEPtr & VM_PHYS_RES_BIT)) {
  1680.     /*
  1681.      * Copy-on-write fault.
  1682.      */
  1683.     retVal = IS_COW;
  1684.     } else if (*curPTEPtr & VM_PHYS_RES_BIT) {
  1685.     /*
  1686.      * The page is already in memory.  Validate it in hardware and set
  1687.      * the reference bit since we are about to reference it.
  1688.      */
  1689.     if (protFault && (*curPTEPtr & VM_COR_CHECK_BIT)) {
  1690.         if (virtAddrPtr->segPtr->type == VM_HEAP) {
  1691.         vmStat.numCORCOWHeapFaults++;
  1692.         } else {
  1693.         vmStat.numCORCOWStkFaults++;
  1694.         }
  1695.         *curPTEPtr &= ~(VM_COR_CHECK_BIT | VM_READ_ONLY_PROT);
  1696.     } else {
  1697.         /* 
  1698.          * Remove "quick" faults from the per-segment counts, so 
  1699.          * that the per-segment counts are more meaningful.
  1700.          */
  1701.         vmStat.quickFaults++;
  1702.         switch (virtAddrPtr->segPtr->type) {
  1703.         case VM_CODE:
  1704.         vmStat.codeFaults--;
  1705.         break;
  1706.         case VM_HEAP:
  1707.         vmStat.heapFaults--;
  1708.         break;
  1709.         case VM_STACK:
  1710.         vmStat.stackFaults--;
  1711.         break;
  1712.         }
  1713.     }
  1714.     if (*curPTEPtr & VM_PREFETCH_BIT) {
  1715.         switch (virtAddrPtr->segPtr->type) {
  1716.         case VM_CODE:
  1717.             vmStat.codePrefetchHits++;
  1718.             break;
  1719.         case VM_HEAP:
  1720.             if (*curPTEPtr & VM_ON_SWAP_BIT) {
  1721.             vmStat.heapSwapPrefetchHits++;
  1722.             } else {
  1723.             vmStat.heapFSPrefetchHits++;
  1724.             }
  1725.             break;
  1726.         case VM_STACK:
  1727.             vmStat.stackPrefetchHits++;
  1728.             break;
  1729.         }
  1730.         *curPTEPtr &= ~VM_PREFETCH_BIT;
  1731.     }
  1732.     VmPageValidateInt(virtAddrPtr, curPTEPtr);
  1733.     *curPTEPtr |= VM_REFERENCED_BIT;
  1734.         retVal = IS_DONE;
  1735.     } else {
  1736.     *curPTEPtr |= VM_IN_PROGRESS_BIT;
  1737.     retVal = NOT_DONE;
  1738.     }
  1739.  
  1740.     UNLOCK_MONITOR;
  1741.     return(retVal);
  1742. }
  1743.  
  1744.  
  1745. /*
  1746.  * ----------------------------------------------------------------------------
  1747.  *
  1748.  * FinishPage --
  1749.  *    This routine finishes the page-in process.  This includes validating
  1750.  *    the page for the currently executing process and releasing the 
  1751.  *    lock on the page.
  1752.  *
  1753.  * Results:
  1754.  *    None.
  1755.  *
  1756.  * Side effects:
  1757.  *    Page-in in progress cleared and lockcount decremented in the
  1758.  *     core map entry.
  1759.  *    
  1760.  *
  1761.  * ----------------------------------------------------------------------------
  1762.  */
  1763. ENTRY static void
  1764. FinishPage(transVirtAddrPtr, ptePtr) 
  1765.     register    Vm_VirtAddr    *transVirtAddrPtr;
  1766.     register    Vm_PTE        *ptePtr;
  1767. {
  1768.     LOCK_MONITOR;
  1769.  
  1770.     /*
  1771.      * Make the page accessible to the user.
  1772.      */
  1773.     VmPageValidateInt(transVirtAddrPtr, ptePtr);
  1774.     coreMap[Vm_GetPageFrame(*ptePtr)].lockCount--;
  1775.     *ptePtr &= ~(VM_ZERO_FILL_BIT | VM_IN_PROGRESS_BIT);
  1776.     /*
  1777.      * Wakeup processes waiting for this pagein to complete.
  1778.      */
  1779.     Sync_Broadcast(&transVirtAddrPtr->segPtr->condition);
  1780.  
  1781.     UNLOCK_MONITOR;
  1782. }
  1783.  
  1784.  
  1785. /*
  1786.  *----------------------------------------------------------------------
  1787.  *
  1788.  * KillCallback --
  1789.  *
  1790.  *    Send a process the SIG_KILL signal when an I/O error
  1791.  *    occurs.  This routine is a callback procedure used by
  1792.  *    VmKillSharers to perform signals without the vm monitor lock
  1793.  *    held.
  1794.  *
  1795.  * Results:
  1796.  *    None.
  1797.  *
  1798.  * Side effects:
  1799.  *    The specified process is killed.
  1800.  *
  1801.  *----------------------------------------------------------------------
  1802.  */
  1803.  
  1804. static void
  1805. KillCallback(data)
  1806.     ClientData data;
  1807. {
  1808.     (void) Sig_Send(SIG_KILL, PROC_VM_READ_ERROR, (Proc_PID) data, FALSE,
  1809.         (Address)0);
  1810. }
  1811.  
  1812. /*
  1813.  * ----------------------------------------------------------------------------
  1814.  *
  1815.  * VmKillSharers --
  1816.  *
  1817.  *    Go down the list of processes sharing this segment and set up
  1818.  *    a callback to send a kill signal to each one without the
  1819.  *    monitor lock held.  This is called when a page from a segment
  1820.  *    couldn't be written to or read from swap space.
  1821.  *
  1822.  * Results:
  1823.  *     None.
  1824.  *
  1825.  * Side effects:
  1826.  *     All processes sharing this segment are destroyed.
  1827.  *    Marks the segment as having an I/O error.
  1828.  *
  1829.  * ----------------------------------------------------------------------------
  1830.  */
  1831.  
  1832. ENTRY void
  1833. VmKillSharers(segPtr) 
  1834.     register    Vm_Segment    *segPtr;
  1835. {
  1836.     register    VmProcLink    *procLinkPtr;
  1837.  
  1838.     LOCK_MONITOR;
  1839.  
  1840.     if ((segPtr->flags & VM_SEG_IO_ERROR) == 0) {
  1841.     LIST_FORALL(segPtr->procList, (List_Links *) procLinkPtr) {
  1842.         Proc_CallFunc(KillCallback,
  1843.               (ClientData) procLinkPtr->procPtr->processID,
  1844.               0);
  1845.     }
  1846.     }
  1847.     segPtr->flags |= VM_SEG_IO_ERROR;
  1848.  
  1849.     UNLOCK_MONITOR;
  1850. }
  1851.  
  1852. static void PinPages _ARGS_((register Vm_VirtAddr *virtAddrPtr, register int lastPage));
  1853.  
  1854.  
  1855. /*
  1856.  * ----------------------------------------------------------------------------
  1857.  *
  1858.  * Vm_PinUserMem --
  1859.  *
  1860.  *      Hardwire pages for all user addresses between firstAddr and
  1861.  *    lastAddr.
  1862.  *
  1863.  * Results:
  1864.  *     SUCCESS if the page-in was successful and SYS_ARG_NO_ACCESS otherwise.
  1865.  *
  1866.  * Side effects:
  1867.  *     Pages between firstAddr and lastAddr are wired down in memory.
  1868.  *
  1869.  * ----------------------------------------------------------------------------
  1870.  */
  1871. ReturnStatus
  1872. Vm_PinUserMem(mapType, numBytes, addr)
  1873.     int             mapType;    /* VM_READONLY_ACCESS | VM_READWRITE_ACCESS */
  1874.     int             numBytes;    /* Number of bytes to map. */
  1875.     register Address addr;    /* Where to start mapping at. */
  1876. {
  1877.     Vm_VirtAddr             virtAddr;
  1878.     ReturnStatus        status = SUCCESS;
  1879.     int                firstPage;
  1880.     int                lastPage;
  1881.     Proc_ControlBlock        *procPtr;
  1882.  
  1883.     procPtr = Proc_GetCurrentProc();
  1884.     VmVirtAddrParse(procPtr, addr, &virtAddr);
  1885.     if (virtAddr.segPtr == (Vm_Segment *)NIL ||
  1886.     (virtAddr.segPtr->type == VM_CODE && mapType == VM_READWRITE_ACCESS)) {
  1887.     return(SYS_ARG_NOACCESS);
  1888.     }
  1889.  
  1890.     firstPage = virtAddr.page;
  1891.     lastPage = ((unsigned)addr + numBytes - 1) >> vmPageShift;
  1892.     while (virtAddr.page <= lastPage) {
  1893.     /*
  1894.      * Loop until we got all of the pages locked down.  We have to
  1895.      * loop because a page could get freed after we touch it but before
  1896.      * we get a chance to wire it down.
  1897.      */
  1898.     status = Vm_TouchPages(virtAddr.page, lastPage - virtAddr.page + 1);
  1899.     if (status != SUCCESS) {
  1900.         goto done;
  1901.     }
  1902.     PinPages(&virtAddr, lastPage);
  1903.     }
  1904.  
  1905.     virtAddr.page = firstPage;
  1906.     VmMach_PinUserPages(mapType,  &virtAddr, lastPage);
  1907.  
  1908. done:
  1909.     if (virtAddr.flags & VM_HEAP_PT_IN_USE) {
  1910.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  1911.     }
  1912.     return(status);
  1913. }
  1914.  
  1915.  
  1916. /*
  1917.  * ----------------------------------------------------------------------------
  1918.  *
  1919.  * PinPages --
  1920.  *
  1921.  *      Hardwire pages for all user addresses between firstAddr and
  1922.  *    lastAddr.
  1923.  *
  1924.  * Results:
  1925.  *      None.
  1926.  *
  1927.  * Side effects:
  1928.  *    virtAddrPtr->page is updated as we successfully wire down pages.  When
  1929.  *    we return its value will be of the last page that we successfully
  1930.  *    wired down + 1.
  1931.  *
  1932.  * ----------------------------------------------------------------------------
  1933.  */
  1934. static void
  1935. PinPages(virtAddrPtr, lastPage)
  1936.     register    Vm_VirtAddr    *virtAddrPtr;
  1937.     register    int        lastPage;
  1938. {
  1939.     register    VmCore    *corePtr;
  1940.     register    Vm_PTE    *ptePtr;
  1941.  
  1942.     LOCK_MONITOR;
  1943.  
  1944.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  1945.          virtAddrPtr->page <= lastPage;
  1946.      VmIncPTEPtr(ptePtr, 1), virtAddrPtr->page++) {
  1947.     while (*ptePtr & VM_IN_PROGRESS_BIT) {
  1948.         (void)Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  1949.     }
  1950.     if (*ptePtr & VM_PHYS_RES_BIT) {
  1951.         corePtr = &coreMap[Vm_GetPageFrame(*ptePtr)];
  1952.         corePtr->wireCount++;
  1953.         corePtr->lockCount++;
  1954.     } else {
  1955.         break;
  1956.     }
  1957.     }
  1958.  
  1959.     UNLOCK_MONITOR;
  1960. }
  1961.  
  1962. static void UnpinPages _ARGS_((Vm_VirtAddr *virtAddrPtr, int lastPage));
  1963.  
  1964.  
  1965. /*
  1966.  * ----------------------------------------------------------------------------
  1967.  *
  1968.  * Vm_UnpinUserMem --
  1969.  *
  1970.  *      Unlock all pages between firstAddr and lastAddr.
  1971.  *    lastAddr.
  1972.  *
  1973.  * Results:
  1974.  *     SUCCESS if the page-in was successful and FAILURE otherwise.
  1975.  *
  1976.  * Side effects:
  1977.  *     None.
  1978.  *
  1979.  * ----------------------------------------------------------------------------
  1980.  */
  1981. void
  1982. Vm_UnpinUserMem(numBytes, addr)
  1983.     int        numBytes;    /* The number of bytes to map. */
  1984.     Address     addr;        /* The address to start mapping at. */
  1985. {
  1986.     Vm_VirtAddr             virtAddr;
  1987.     Proc_ControlBlock        *procPtr;
  1988.     int                lastPage;
  1989.  
  1990.     procPtr = Proc_GetCurrentProc();
  1991.     VmVirtAddrParse(procPtr, addr, &virtAddr);
  1992.     lastPage = (unsigned int)(addr + numBytes - 1) >> vmPageShift;
  1993.     /*
  1994.      * Now unlock all of the pages.
  1995.      */
  1996.     VmMach_UnpinUserPages(&virtAddr, lastPage);
  1997.     UnpinPages(&virtAddr, lastPage);
  1998.  
  1999.     if (virtAddr.flags & VM_HEAP_PT_IN_USE) {
  2000.     /*
  2001.      * The heap segment has been made not expandable by VmVirtAddrParse
  2002.      * so that the address parse would remain valid.  Decrement the
  2003.      * in use count now.
  2004.      */
  2005.     VmDecPTUserCount(procPtr->vmPtr->segPtrArray[VM_HEAP]);
  2006.     }
  2007. }
  2008.  
  2009.  
  2010. /*
  2011.  * ----------------------------------------------------------------------------
  2012.  *
  2013.  * UnpinPages --
  2014.  *
  2015.  *      Unlock pages for all user addresses between firstAddr and
  2016.  *    lastAddr.
  2017.  *
  2018.  * Results:
  2019.  *    None
  2020.  *
  2021.  * Side effects:
  2022.  *    Core map entry lock count and flags field may be modified.
  2023.  *
  2024.  * ----------------------------------------------------------------------------
  2025.  */
  2026. static void
  2027. UnpinPages(virtAddrPtr, lastPage)
  2028.     Vm_VirtAddr    *virtAddrPtr;
  2029.     int        lastPage;
  2030. {
  2031.     register    VmCore    *corePtr;
  2032.     register    Vm_PTE    *ptePtr;
  2033.     register    int    i;
  2034.  
  2035.     LOCK_MONITOR;
  2036.  
  2037.     for (i = virtAddrPtr->page,
  2038.         ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2039.      i <= lastPage;
  2040.      VmIncPTEPtr(ptePtr, 1), i++) {
  2041.  
  2042.     while (*ptePtr & VM_IN_PROGRESS_BIT) {
  2043.         (void)Sync_Wait(&virtAddrPtr->segPtr->condition, FALSE);
  2044.     }
  2045.  
  2046.     if (*ptePtr & VM_PHYS_RES_BIT) {
  2047.         corePtr = &coreMap[Vm_GetPageFrame(*ptePtr)];
  2048.         if (corePtr->wireCount > 0) {
  2049.         corePtr->wireCount--;
  2050.         corePtr->lockCount--;
  2051.         }
  2052.     }
  2053.     }
  2054.  
  2055.     UNLOCK_MONITOR;
  2056. }
  2057.  
  2058.  
  2059.  
  2060. /*
  2061.  * ----------------------------------------------------------------------------
  2062.  *
  2063.  * VmPagePinned --
  2064.  *
  2065.  *      Return TRUE if the page is wired down in memory and FALSE otherwise.
  2066.  *
  2067.  * Results:
  2068.  *     TRUE if the page is wired down and FALSE otherwise.
  2069.  *
  2070.  * Side effects:
  2071.  *     None.
  2072.  *
  2073.  * ----------------------------------------------------------------------------
  2074.  */
  2075. INTERNAL Boolean
  2076. VmPagePinned(ptePtr)
  2077.     Vm_PTE    *ptePtr;
  2078. {
  2079.     return(coreMap[Vm_GetPageFrame(*ptePtr)].wireCount > 0);
  2080. }
  2081.  
  2082.  
  2083. /*----------------------------------------------------------------------------
  2084.  *
  2085.  *         Routines for writing out dirty pages        
  2086.  *
  2087.  * Dirty pages are written to the swap file by the function PageOut.
  2088.  * PageOut is called by using the Proc_CallFunc routine which invokes
  2089.  * a process on PageOut.  When a page is put onto the dirty list a new
  2090.  * incantation of PageOut will be created unless there are already
  2091.  * more than vmMaxPageOutProcs already writing out the dirty list.  Thus the
  2092.  * dirty list will be cleaned by at most vmMaxPageOutProcs working in parallel.
  2093.  *
  2094.  * The work done by PageOut is split into work done at non-monitor level and
  2095.  * monitor level.  It calls the monitored routine PageOutPutAndGet to get the 
  2096.  * next page off of the dirty list.  It then writes the page out to the 
  2097.  * file server at non-monitor level.  Next it calls the monitored routine 
  2098.  * PageOutPutAndGet to put the page onto the front of the allocate list and
  2099.  * get the next dirty page.  Finally when there are no more pages to clean it
  2100.  * returns (and dies).
  2101.  */
  2102.  
  2103. static void PageOutPutAndGet _ARGS_((VmCore **corePtrPtr, ReturnStatus status, Boolean *doRecoveryPtr, Fs_Stream **recStreamPtrPtr));
  2104. static void PutOnFront _ARGS_((register VmCore *corePtr));
  2105.  
  2106. /*
  2107.  * ----------------------------------------------------------------------------
  2108.  *
  2109.  * PageOut --
  2110.  *
  2111.  *    Function to write out pages on dirty list.  It will keep retrieving
  2112.  *    pages from the dirty list until there are no more left.  This function
  2113.  *    is designed to be called through Proc_CallFunc.
  2114.  *    
  2115.  * Results:
  2116.  *         None.
  2117.  *
  2118.  * Side effects:
  2119.  *         The dirty list is emptied.
  2120.  *
  2121.  * ----------------------------------------------------------------------------
  2122.  */
  2123. /* ARGSUSED */
  2124. static void
  2125. PageOut(data, callInfoPtr)
  2126.     ClientData        data;        /* Ignored. */
  2127.     Proc_CallInfo    *callInfoPtr;    /* Ignored. */
  2128. {
  2129.     VmCore        *corePtr;
  2130.     ReturnStatus    status = SUCCESS;
  2131.     Fs_Stream        *recoveryStreamPtr;
  2132.     Boolean        doRecovery;
  2133.     Boolean        returnSwapStream;
  2134.  
  2135.     vmStat.pageoutWakeup++;
  2136.  
  2137.     corePtr = (VmCore *) NIL;
  2138.     while (TRUE) {
  2139.     doRecovery = FALSE;
  2140.     PageOutPutAndGet(&corePtr, status, &doRecovery, &recoveryStreamPtr);
  2141.     if (doRecovery) {
  2142.         /*
  2143.          * The following shenanigans are used to carefully
  2144.          * synchronize access to the swap directory stream.
  2145.          */
  2146.         returnSwapStream = FALSE;
  2147.         if (recoveryStreamPtr == (Fs_Stream  *)NIL) {
  2148.         recoveryStreamPtr = VmGetSwapStreamPtr();
  2149.         if (recoveryStreamPtr != (Fs_Stream *)NIL) {
  2150.             returnSwapStream = TRUE;
  2151.         }
  2152.         }
  2153.         if (recoveryStreamPtr != (Fs_Stream  *)NIL) {
  2154.         (void)Fsutil_WaitForHost(recoveryStreamPtr, FS_NON_BLOCKING,
  2155.                      status);
  2156.         if (returnSwapStream) {
  2157.             VmDoneWithSwapStreamPtr();
  2158.         }
  2159.         }
  2160.     }
  2161.  
  2162.     if (corePtr == (VmCore *) NIL) {
  2163.         break;
  2164.     }
  2165.     status = VmPageServerWrite(&corePtr->virtPage, 
  2166.                    (unsigned int) (corePtr - coreMap),
  2167.                    FALSE);
  2168.     if (status != SUCCESS) {
  2169.         if ( ! VmSwapStreamOk() ||
  2170.             (status != RPC_TIMEOUT && status != FS_STALE_HANDLE &&
  2171.          status != RPC_SERVICE_DISABLED)) {
  2172.         /*
  2173.          * Non-recoverable error on page write, so kill all users of 
  2174.          * this segment.
  2175.          */
  2176.         VmKillSharers(corePtr->virtPage.segPtr);
  2177.         }
  2178.     }
  2179.     }
  2180.  
  2181. }
  2182.  
  2183.  
  2184. /*
  2185.  * ----------------------------------------------------------------------------
  2186.  *
  2187.  * PageOutPutAndGet --
  2188.  *
  2189.  *    This routine does two things.  First it puts the page pointed to by
  2190.  *    *corePtrPtr (if any) onto the front of the allocate list and wakes
  2191.  *    up any dying processes waiting for this page to be cleaned.
  2192.  *    It then takes the first page off of the dirty list and returns a 
  2193.  *    pointer to it.  Before returning the pointer it clears the 
  2194.  *      modified bit of the page frame.
  2195.  *
  2196.  * Results:
  2197.  *     A pointer to the first page on the dirty list.  If there are no pages
  2198.  *     then *corePtrPtr is set to NIL.
  2199.  *
  2200.  * Side effects:
  2201.  *    The dirty list and allocate lists may both be modified.  In addition
  2202.  *      the onSwap bit is set to indicate that the page is now on swap space.
  2203.  *
  2204.  * ----------------------------------------------------------------------------
  2205.  */
  2206. ENTRY static void
  2207. PageOutPutAndGet(corePtrPtr, status, doRecoveryPtr, recStreamPtrPtr)
  2208.     VmCore     **corePtrPtr;        /* On input points to page frame
  2209.                      * to be put back onto allocate list.
  2210.                      * On output points to page frame
  2211.                      * to be cleaned. */
  2212.     ReturnStatus status;        /* Status from the write. */
  2213.     Boolean    *doRecoveryPtr;        /* Return.  TRUE if recovery should
  2214.                      * be attempted.  In this case check
  2215.                      * *recStreamPtrPtr and wait for
  2216.                      * recovery on that, or wait for
  2217.                      * recovery on vmSwapStreamPtr. */
  2218.     Fs_Stream     **recStreamPtrPtr;    /* Pointer to stream to do recovery
  2219.                      * on if *doRecoveryPtr is TRUE.  If
  2220.                      * this is still NIL, then do recovery
  2221.                      * on vmSwapStreamPtr instead */
  2222. {
  2223.     register    Vm_PTE    *ptePtr;
  2224.     register    VmCore    *corePtr;
  2225.  
  2226.     LOCK_MONITOR;
  2227.  
  2228.     *doRecoveryPtr = FALSE;
  2229.     *recStreamPtrPtr = (Fs_Stream *)NIL;
  2230.     corePtr = *corePtrPtr;
  2231.     if (corePtr == (VmCore *)NIL) {
  2232.     if (swapDown) {
  2233.         numPageOutProcs--;
  2234.         UNLOCK_MONITOR;
  2235.         return;
  2236.     }
  2237.     } else {
  2238.     switch (status) {
  2239.         case RPC_TIMEOUT:
  2240.         case RPC_SERVICE_DISABLED:
  2241.         case FS_STALE_HANDLE: {
  2242.             if (!swapDown) {
  2243.             /*
  2244.              * We have not realized that we have an error yet.
  2245.              * Mark the swap server as down, and return a
  2246.              * pointer to the swap stream.  If it isn't open
  2247.              * yet we'll return NIL, and our caller should use
  2248.              * vmSwapStreamPtr for recovery, which is guarded
  2249.              * by a different monitor.
  2250.              */
  2251.             *recStreamPtrPtr = corePtr->virtPage.segPtr->swapFilePtr;
  2252.             *doRecoveryPtr = TRUE;
  2253.             swapDown = TRUE;
  2254.             }
  2255.             corePtr->flags &= ~VM_PAGE_BEING_CLEANED;
  2256.             VmListInsert((List_Links *)corePtr,
  2257.                  LIST_ATREAR(dirtyPageList));
  2258.             *corePtrPtr = (VmCore *)NIL;
  2259.             numPageOutProcs--;
  2260.             UNLOCK_MONITOR;
  2261.             return;
  2262.         }
  2263.         break;
  2264.         default:
  2265.         break;
  2266.     }
  2267.     PutOnFront(corePtr);
  2268.     corePtr = (VmCore *) NIL;    
  2269.     }
  2270.  
  2271.     while (!List_IsEmpty(dirtyPageList)) {
  2272.         /*
  2273.      * Get the first page off of the dirty list.
  2274.      */
  2275.     corePtr = (VmCore *) List_First(dirtyPageList);
  2276.     VmListRemove((List_Links *) corePtr);
  2277.     /*
  2278.      * If this segment is being deleted then invalidate the page and
  2279.      * then free it.
  2280.      */
  2281.         if (corePtr->virtPage.segPtr->flags & VM_SEG_DEAD) {
  2282.         vmStat.numDirtyPages--;
  2283.         VmPageInvalidateInt(&corePtr->virtPage,
  2284.         VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page));
  2285.         PutOnFreeList(corePtr);
  2286.         corePtr = (VmCore *) NIL;
  2287.     } else {
  2288.         break;
  2289.     }
  2290.     }
  2291.  
  2292.     if (corePtr != (VmCore *) NIL) {
  2293.     /*
  2294.      * This page will now be on the page server so set the pte accordingly.
  2295.      * In addition the modified bit must be cleared here since the page
  2296.      * could get modified while it is being cleaned.
  2297.      */
  2298.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page);
  2299.     *ptePtr |= VM_ON_SWAP_BIT;
  2300.     /*
  2301.      * If the page has become locked while it was on the dirty list, don't
  2302.      * clear the modify bit.  The set modify bit after the page write 
  2303.      * completes will cause this page to be put back on the alloc list.
  2304.      */
  2305.     if (corePtr->lockCount == 0) {
  2306.         *ptePtr &= ~VM_MODIFIED_BIT;
  2307.         VmMach_ClearModBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr));
  2308.     }
  2309.     corePtr->flags |= VM_PAGE_BEING_CLEANED;
  2310.     } else {
  2311.     /*
  2312.      * No dirty pages.  Decrement the number of page out procs and
  2313.      * return nil.  PageOut will kill itself when it receives NIL.
  2314.      */
  2315.     numPageOutProcs--;
  2316.  
  2317.     if (numPageOutProcs == 0 && vmStat.numDirtyPages != 0) {
  2318.         panic("PageOutPutAndGet: Dirty pages but no pageout procs\n");
  2319.     }
  2320.     }
  2321.  
  2322.     *corePtrPtr = corePtr;
  2323.  
  2324.     UNLOCK_MONITOR;
  2325. }
  2326.  
  2327.  
  2328. /*
  2329.  * ----------------------------------------------------------------------------
  2330.  *
  2331.  * PutOnFront --
  2332.  *
  2333.  *    Take one of two actions.  If page frame is already marked as free
  2334.  *    then put it onto the front of the free list.  Otherwise put it onto
  2335.  *    the front of the allocate list.  
  2336.  *
  2337.  * Results:
  2338.  *    None.    
  2339.  *
  2340.  * Side effects:
  2341.  *    Allocate list or free list modified.
  2342.  *
  2343.  * ----------------------------------------------------------------------------
  2344.  */
  2345. INTERNAL static void
  2346. PutOnFront(corePtr)
  2347.     register    VmCore    *corePtr;
  2348. {
  2349.     register    Vm_PTE    *ptePtr;
  2350.  
  2351.     if (corePtr->flags & VM_SEG_PAGEOUT_WAIT) {
  2352.     Sync_Broadcast(&corePtr->virtPage.segPtr->condition);
  2353.     }
  2354.     corePtr->flags &= ~(VM_DIRTY_PAGE | VM_PAGE_BEING_CLEANED | 
  2355.                 VM_SEG_PAGEOUT_WAIT | VM_DONT_FREE_UNTIL_CLEAN);
  2356.     if (corePtr->flags & VM_FREE_PAGE) {
  2357.     PutOnFreeList(corePtr);
  2358.     } else {
  2359.     Boolean    referenced, modified;
  2360.     /*
  2361.      * Update the software page table from the hardware.  This 
  2362.      * catches the case when a page that we are writting out is
  2363.      * modified.  
  2364.      */
  2365.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage,
  2366.                  corePtr->virtPage.page);
  2367.     referenced = *ptePtr & VM_REFERENCED_BIT;
  2368.     modified = *ptePtr & VM_MODIFIED_BIT;
  2369.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2370.               &referenced, &modified);
  2371.     if (referenced) {
  2372.         *ptePtr |= (VM_REFERENCED_BIT);
  2373.     }
  2374.     if (modified) {
  2375.         *ptePtr |= (VM_REFERENCED_BIT|VM_MODIFIED_BIT);
  2376.     }
  2377.     if (vmFreeWhenClean && corePtr->lockCount == 0 && 
  2378.                     !(*ptePtr & VM_REFERENCED_BIT)) {
  2379.         /*
  2380.          * We are supposed to free pages after we clean them.  Before
  2381.          * we put this page onto the dirty list, we already invalidated
  2382.          * it in hardware, thus forcing it to be faulted on before being
  2383.          * referenced.  If it was faulted on then PreparePage would have
  2384.          * set the reference bit in the PTE.  Thus if the reference bit
  2385.          * isn't set then the page isn't valid and thus it couldn't
  2386.          * possibly have been modified or referenced.  So we free this
  2387.          * page.
  2388.          */
  2389.          if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2390.            panic("PutOnFront: Resident bit not set\n");
  2391.          }
  2392.          corePtr->virtPage.segPtr->resPages--;
  2393.          *ptePtr &= ~(VM_PHYS_RES_BIT | VM_PAGE_FRAME_FIELD);
  2394.         PutOnFreeList(corePtr);
  2395.     } else {
  2396.         PutOnAllocListFront(corePtr);
  2397.     }
  2398.     }
  2399.     vmStat.numDirtyPages--; 
  2400.     Sync_Broadcast(&cleanCondition);
  2401. }
  2402.  
  2403.  
  2404. /*
  2405.  * Variables for the clock daemon.  vmPagesToCheck is the number of page 
  2406.  * frames to examine each time that the clock daemon wakes up.  vmClockSleep
  2407.  * is the amount of time for the clock daemon before it runs again.
  2408.  */
  2409. unsigned int    vmClockSleep;        
  2410. int        vmPagesToCheck = 100;
  2411. static    int    clockHand = 0;
  2412.  
  2413. /*
  2414.  * ----------------------------------------------------------------------------
  2415.  *
  2416.  * Vm_Clock --
  2417.  *
  2418.  *    Main loop for the clock daemon process.  It will wakeup every 
  2419.  *    few seconds, examine a few page frames, and then go back to sleep.
  2420.  *    It is used to keep the allocate list in approximate LRU order.
  2421.  *
  2422.  * Results:
  2423.  *         None.
  2424.  *
  2425.  * Side effects:
  2426.  *         The allocate list is modified.
  2427.  *
  2428.  * ----------------------------------------------------------------------------
  2429.  */
  2430. /*ARGSUSED*/
  2431. ENTRY void
  2432. Vm_Clock(data, callInfoPtr)
  2433.     ClientData    data;
  2434.     Proc_CallInfo    *callInfoPtr;
  2435. {
  2436.     static Boolean initialized = FALSE;
  2437.  
  2438.     register    VmCore    *corePtr;
  2439.     register    Vm_PTE    *ptePtr;
  2440.     int            i;
  2441.     Time        curTime;
  2442.     Boolean        referenced;
  2443.     Boolean        modified;
  2444.  
  2445.     LOCK_MONITOR;
  2446.  
  2447.     Timer_GetTimeOfDay(&curTime, (int *) NIL, (Boolean *) NIL);
  2448.  
  2449.     if (vmTraceNeedsInit) {
  2450.     short    initEnd;
  2451.  
  2452.     vmTraceTime = 0;
  2453.     VmTraceSegStart();
  2454.     VmMach_Trace();
  2455.     VmStoreTraceRec(VM_TRACE_END_INIT_REC, sizeof(short),
  2456.             (Address)&initEnd, TRUE);
  2457.     vmTracesToGo = vmTracesPerClock;
  2458.     vmClockSleep = timer_IntOneSecond / vmTracesPerClock;
  2459.     vm_Tracing = TRUE;
  2460.     vmTraceNeedsInit = FALSE;
  2461.     } else if (vm_Tracing) {
  2462.     vmTraceStats.numTraces++;
  2463.     VmMach_Trace();
  2464.  
  2465.     /*
  2466.      * Decrement the number of traces per iteration of the clock.  If we
  2467.      * are at 0 then run the clock for one iteration.
  2468.      */
  2469.     vmTracesToGo--;
  2470.     if (vmTracesToGo > 0) {
  2471.         callInfoPtr->interval = vmClockSleep;
  2472.         UNLOCK_MONITOR;
  2473.         return;
  2474.     }
  2475.     vmTracesToGo = vmTracesPerClock;
  2476.     }
  2477.  
  2478.     /*
  2479.      * Examine vmPagesToCheck pages.
  2480.      */
  2481.  
  2482.     for (i = 0; i < vmPagesToCheck; i++) {
  2483.     corePtr = &(coreMap[clockHand]);
  2484.  
  2485.     /*
  2486.      * Move to the next page in the core map.  If have reached the
  2487.      * end of the core map then go back to the first page that may not
  2488.      * be used by the kernel.
  2489.      */
  2490.     if (clockHand == vmStat.numPhysPages - 1) {
  2491.         clockHand = vmFirstFreePage;
  2492.     } else {
  2493.         clockHand++;
  2494.     }
  2495.  
  2496.     /*
  2497.      * If the page is free, locked, in the middle of a page-in, 
  2498.      * or in the middle of a pageout, then we aren't concerned 
  2499.      * with this page.
  2500.      */
  2501.     if ((corePtr->flags & (VM_DIRTY_PAGE | VM_FREE_PAGE)) ||
  2502.         corePtr->lockCount > 0) {
  2503.         continue;
  2504.     }
  2505.  
  2506.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page);
  2507.  
  2508.     /*
  2509.      * If the page has been referenced, then put it on the end of the
  2510.      * allocate list.
  2511.      */
  2512.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2513.                  &referenced, &modified);
  2514.     if ((*ptePtr & VM_REFERENCED_BIT) || referenced) {
  2515.         VmListMove((List_Links *) corePtr, LIST_ATREAR(allocPageList));
  2516.         corePtr->lastRef = curTime.seconds;
  2517.         *ptePtr &= ~VM_REFERENCED_BIT;
  2518.         VmMach_ClearRefBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr));
  2519.         if (vmWriteableRefPageout &&
  2520.         corePtr->virtPage.segPtr->type != VM_CODE) {
  2521.         *ptePtr |= VM_MODIFIED_BIT;
  2522.         }
  2523.     }
  2524.     }
  2525.  
  2526.     if (!initialized) {
  2527.         vmClockSleep = timer_IntOneSecond;
  2528.     initialized = TRUE;
  2529.     }
  2530.  
  2531.     callInfoPtr->interval = vmClockSleep;
  2532.  
  2533.     UNLOCK_MONITOR;
  2534. }
  2535.  
  2536. /*
  2537.  * ----------------------------------------------------------------------------
  2538.  *
  2539.  * VmCountDirtyPages --
  2540.  *
  2541.  *    Return the number of dirty pages.
  2542.  *
  2543.  * Results:
  2544.  *         None.
  2545.  *
  2546.  * Side effects:
  2547.  *         The allocate list is modified.
  2548.  *
  2549.  * ----------------------------------------------------------------------------
  2550.  */
  2551. ENTRY int
  2552. VmCountDirtyPages()
  2553. {
  2554.     register    Vm_PTE    *ptePtr;
  2555.     register    VmCore    *corePtr;
  2556.     register    int    i;
  2557.     register    int    numDirtyPages = 0;
  2558.     Boolean        referenced;
  2559.     Boolean        modified;
  2560.  
  2561.     LOCK_MONITOR;
  2562.  
  2563.     for (corePtr = &coreMap[vmFirstFreePage], i = vmFirstFreePage;
  2564.          i < vmStat.numPhysPages;
  2565.      i++, corePtr++) {
  2566.     if ((corePtr->flags & VM_FREE_PAGE) || corePtr->lockCount > 0) {
  2567.         continue;
  2568.     }
  2569.     if (corePtr->flags & VM_DIRTY_PAGE) {
  2570.         numDirtyPages++;
  2571.         continue;
  2572.     }
  2573.     ptePtr = VmGetAddrPTEPtr(&corePtr->virtPage, corePtr->virtPage.page);
  2574.     if (*ptePtr & VM_MODIFIED_BIT) {
  2575.         numDirtyPages++;
  2576.         continue;
  2577.     }
  2578.     VmMach_GetRefModBits(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr),
  2579.                  &referenced, &modified);
  2580.     if (modified) {
  2581.         numDirtyPages++;
  2582.     }
  2583.     }
  2584.     UNLOCK_MONITOR;
  2585.     return(numDirtyPages);
  2586. }
  2587.  
  2588. /*
  2589.  * ----------------------------------------------------------------------------
  2590.  *
  2591.  * VmFlushSegment --
  2592.  *
  2593.  *    Flush the given range of pages in the segment to swap space and take
  2594.  *    them out of memory.  It is assumed that the processes that own this
  2595.  *    segment are frozen.
  2596.  *
  2597.  * Results:
  2598.  *         None.
  2599.  *
  2600.  * Side effects:
  2601.  *         All memory in the given range is forced out to swap and freed.
  2602.  *    *virtAddrPtr is modified.
  2603.  *
  2604.  * ----------------------------------------------------------------------------
  2605.  */
  2606. ENTRY void
  2607. VmFlushSegment(virtAddrPtr, lastPage)
  2608.     Vm_VirtAddr    *virtAddrPtr;
  2609.     int        lastPage;
  2610. {
  2611.     register    Vm_PTE        *ptePtr;
  2612.     register    VmCore        *corePtr;
  2613.     unsigned int        pfNum;
  2614.     Boolean            referenced;
  2615.     Boolean            modified;
  2616.  
  2617.     LOCK_MONITOR;
  2618.  
  2619.     if (virtAddrPtr->segPtr->ptPtr == (Vm_PTE *)NIL) {
  2620.     UNLOCK_MONITOR;
  2621.     return;
  2622.     }
  2623.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2624.          virtAddrPtr->page <= lastPage;
  2625.      virtAddrPtr->page++, VmIncPTEPtr(ptePtr, 1)) {
  2626.     if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2627.         continue;
  2628.     }
  2629.     pfNum = Vm_GetPageFrame(*ptePtr);
  2630.     corePtr = &coreMap[pfNum];
  2631.     if (corePtr->lockCount > 0) {
  2632.         continue;
  2633.     }
  2634.     if (corePtr->flags & VM_DIRTY_PAGE) {
  2635.         corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  2636.     } else {
  2637.         VmMach_GetRefModBits(virtAddrPtr, Vm_GetPageFrame(*ptePtr),
  2638.                  &referenced, &modified);
  2639.         if ((*ptePtr & VM_MODIFIED_BIT) || modified) {
  2640.         TakeOffAllocList(corePtr);
  2641.         PutOnDirtyList(corePtr);
  2642.         corePtr->flags |= VM_DONT_FREE_UNTIL_CLEAN;
  2643.         }
  2644.     }
  2645.     VmPageFreeInt(pfNum);
  2646.     VmPageInvalidateInt(virtAddrPtr, ptePtr);
  2647.     }
  2648.  
  2649.     UNLOCK_MONITOR;
  2650. }
  2651.  
  2652.  
  2653. /*
  2654.  *----------------------------------------------------------------------
  2655.  *
  2656.  * Vm_GetPageSize --
  2657.  *
  2658.  *      Return the page size.
  2659.  *
  2660.  * Results:
  2661.  *      The page size.
  2662.  *
  2663.  * Side effects:
  2664.  *      None.
  2665.  *
  2666.  *----------------------------------------------------------------------
  2667.  */
  2668. int
  2669. Vm_GetPageSize()
  2670. {
  2671.     return(vm_PageSize);
  2672. }
  2673.  
  2674.  
  2675. /*
  2676.  *----------------------------------------------------------------------
  2677.  *
  2678.  * Vm_MapBlock --
  2679.  *
  2680.  *      Allocate and validate enough pages at the given address to map
  2681.  *    one FS cache block.
  2682.  *
  2683.  * Results:
  2684.  *      The number of pages that were allocated.
  2685.  *
  2686.  * Side effects:
  2687.  *      Pages added to kernels address space.
  2688.  *
  2689.  *----------------------------------------------------------------------
  2690.  */
  2691. int
  2692. Vm_MapBlock(addr)
  2693.     Address    addr;    /* Address where to map in pages. */
  2694. {
  2695.     register    Vm_PTE    *ptePtr;
  2696.     Vm_VirtAddr        virtAddr;
  2697.     unsigned    int    page;
  2698.     int            curFSPages;
  2699.  
  2700.     vmStat.fsMap++;
  2701.     curFSPages = vmStat.fsMap - vmStat.fsUnmap;
  2702.     if (curFSPages >= vmBoundary) {
  2703.     vmBoundary += vmPagesPerGroup;
  2704.     vmCurPenalty += vmFSPenalty;
  2705.     }
  2706.     if (curFSPages > vmStat.maxFSPages) {
  2707.     vmStat.maxFSPages = curFSPages;
  2708.     }
  2709.  
  2710.     virtAddr.page = (unsigned int) addr >> vmPageShift;
  2711.     virtAddr.offset = 0;
  2712.     virtAddr.segPtr = vm_SysSegPtr;
  2713.     virtAddr.flags = 0;
  2714.     virtAddr.sharedPtr = (Vm_SegProcList *)NIL;
  2715.     ptePtr = VmGetPTEPtr(vm_SysSegPtr, virtAddr.page);
  2716.  
  2717.     /*
  2718.      * Allocate a block.  We know that the page size is not smaller than
  2719.      * the block size so that one page will suffice.
  2720.      */
  2721.     page = DoPageAllocate(&virtAddr, 0);
  2722.     if (page == VM_NO_MEM_VAL) {
  2723.     /*
  2724.      * Couldn't get any memory.  
  2725.      */
  2726.     return(0);
  2727.     }
  2728.     *ptePtr |= page;
  2729.     VmPageValidate(&virtAddr);
  2730.  
  2731.     return(1);
  2732. }
  2733.  
  2734.  
  2735. /*
  2736.  *----------------------------------------------------------------------
  2737.  *
  2738.  * Vm_UnmapBlock --
  2739.  *
  2740.  *      Free and invalidate enough pages at the given address to unmap
  2741.  *    one fs cache block.
  2742.  *
  2743.  * Results:
  2744.  *      The number of pages that were deallocated.
  2745.  *
  2746.  * Side effects:
  2747.  *      Pages removed from kernels address space.
  2748.  *
  2749.  *----------------------------------------------------------------------
  2750.  */
  2751. int
  2752. Vm_UnmapBlock(addr, retOnePage, pageNumPtr)
  2753.     Address    addr;        /* Address where to map in pages. */
  2754.     Boolean    retOnePage;    /* TRUE => don't put one of the pages on
  2755.                  * the free list and return its value in
  2756.                  * *pageNumPtr. */
  2757.     unsigned int *pageNumPtr;    /* One of the pages that was unmapped. */
  2758. {
  2759.     register    Vm_PTE    *ptePtr;
  2760.     Vm_VirtAddr        virtAddr;
  2761.     int            curFSPages;
  2762.  
  2763.     vmStat.fsUnmap++;
  2764.     curFSPages = vmStat.fsMap - vmStat.fsUnmap;
  2765.  
  2766.     if (curFSPages < vmBoundary) {
  2767.     vmBoundary -= vmPagesPerGroup;
  2768.     vmCurPenalty -= vmFSPenalty;
  2769.     }
  2770.     if (curFSPages < vmStat.minFSPages) {
  2771.     vmStat.minFSPages = curFSPages;
  2772.     }
  2773.  
  2774.     virtAddr.page = (unsigned int) addr >> vmPageShift;
  2775.     virtAddr.offset = 0;
  2776.     virtAddr.segPtr = vm_SysSegPtr;
  2777.     virtAddr.flags = 0;
  2778.     virtAddr.sharedPtr = (Vm_SegProcList *)NIL;
  2779.     ptePtr = VmGetPTEPtr(vm_SysSegPtr, virtAddr.page);
  2780.  
  2781.     if (retOnePage) {
  2782.     *pageNumPtr = Vm_GetPageFrame(*ptePtr);
  2783.     } else {
  2784.     /*
  2785.      * If we aren't supposed to return the page, then free it.
  2786.      */
  2787.     VmPageFree(Vm_GetPageFrame(*ptePtr));
  2788.     }
  2789.     VmPageInvalidate(&virtAddr);
  2790.  
  2791.     return(1);
  2792. }
  2793.  
  2794.  
  2795. /*
  2796.  *----------------------------------------------------------------------
  2797.  *
  2798.  * Vm_FsCacheSize --
  2799.  *
  2800.  *    Return the virtual addresses of the start and end of the file systems
  2801.  *    cache.
  2802.  *
  2803.  * Results:
  2804.  *    None.
  2805.  *
  2806.  * Side effects:
  2807.  *    None.
  2808.  *
  2809.  *----------------------------------------------------------------------
  2810.  */
  2811. void
  2812. Vm_FsCacheSize(startAddrPtr, endAddrPtr)
  2813.     Address    *startAddrPtr;    /* Lowest virtual address. */
  2814.     Address    *endAddrPtr;    /* Highest virtual address. */
  2815. {
  2816.     int    numPages;
  2817.  
  2818.     /*
  2819.      * Compute the minimum number of pages that are reserved for VM.  The number
  2820.      * of free pages is the maximum number of pages that will ever exist
  2821.      * for user processes.
  2822.      */
  2823.     vmStat.minVMPages = vmStat.numFreePages / MIN_VM_PAGE_FRACTION;
  2824.     vmMaxDirtyPages = vmStat.numFreePages / MAX_DIRTY_PAGE_FRACTION;
  2825.  
  2826.     *startAddrPtr = vmBlockCacheBaseAddr;
  2827.     /*
  2828.      * We aren't going to get any more free pages so limit the maximum number
  2829.      * of blocks in the cache to the number of free pages that we have minus
  2830.      * the minimum amount of free pages that we keep for user
  2831.      * processes to run.
  2832.      */
  2833.     numPages = ((unsigned int)vmBlockCacheEndAddr - 
  2834.         (unsigned int)vmBlockCacheBaseAddr) / vm_PageSize;
  2835.     if (numPages > vmStat.numFreePages - vmStat.minVMPages) {
  2836.     numPages = vmStat.numFreePages - vmStat.minVMPages;
  2837.     }
  2838.     *endAddrPtr = vmBlockCacheBaseAddr + numPages * vm_PageSize - 1;
  2839.     /*
  2840.      * Compute the penalties to put onto FS pages.
  2841.      */
  2842.     vmPagesPerGroup = vmStat.numFreePages / vmNumPageGroups;
  2843.     vmCurPenalty = 0;
  2844.     vmBoundary = vmPagesPerGroup;
  2845. }
  2846.  
  2847. /*---------------------------------------------------------------------------
  2848.  * 
  2849.  *    Routines for recovery
  2850.  *
  2851.  * VM needs to be able to recover when the server of swap crashes.  This is
  2852.  * done in the following manner:
  2853.  *
  2854.  *    1) At boot time the directory "/swap/host_number" is open by the
  2855.  *     routine Vm_OpenSwapDirectory and the stream is stored in 
  2856.  *     vmSwapStreamPtr.
  2857.  *    2) If an error occurs on a page write then the variable swapDown
  2858.  *     is set to TRUE which prohibits all further actions that would dirty
  2859.  *     physical memory pages (e.g. page faults) and prohibits dirty pages
  2860.  *     from being written to swap.
  2861.  *    3) Next the routine Fsutil_WaitForHost is called to asynchronously wait
  2862.  *     for the server to come up.  When it detects that the server is
  2863.  *     in fact up and the file system is alive, it calls Vm_Recovery.
  2864.  *    4) Vm_Recovery when called will set swapDown to FALSE and start cleaning
  2865.  *     dirty pages if necessary.
  2866.  */
  2867.  
  2868.  
  2869. /*
  2870.  *----------------------------------------------------------------------
  2871.  *
  2872.  * Vm_Recovery --
  2873.  *
  2874.  *    The swap area has just come back up.  Wake up anyone waiting for it to
  2875.  *    come back and start up page cleaners if there are dirty pages to be
  2876.  *    written out.
  2877.  *
  2878.  * Results:
  2879.  *    None.
  2880.  *
  2881.  * Side effects:
  2882.  *    swapDown flag set to FALSE.
  2883.  *
  2884.  *----------------------------------------------------------------------
  2885.  */
  2886. ENTRY void
  2887. Vm_Recovery()
  2888. {
  2889.     LOCK_MONITOR;
  2890.  
  2891.     swapDown = FALSE;
  2892.     Sync_Broadcast(&swapDownCondition);
  2893.     while (vmStat.numDirtyPages - numPageOutProcs > 0 &&
  2894.        numPageOutProcs < vmMaxPageOutProcs) { 
  2895.     Proc_CallFunc(PageOut, (ClientData) numPageOutProcs, 0);
  2896.     numPageOutProcs++;
  2897.     }
  2898.  
  2899.     UNLOCK_MONITOR;
  2900. }
  2901.  
  2902. /*
  2903.  *----------------------------------------------------------------------
  2904.  *
  2905.  * VmPageFlush --
  2906.  *
  2907.  *    Flush (shared) pages to the server or disk.
  2908.  *
  2909.  * Results:
  2910.  *    SUCCESS if it worked.
  2911.  *
  2912.  * Side effects:
  2913.  *    Page is written to disk and removed from memory.
  2914.  *    If page is pinned down, it will be unpinned.
  2915.  *    The page is invalidated from the local cache.
  2916.  *    *virtAddrPtr is modified.
  2917.  *
  2918.  *----------------------------------------------------------------------
  2919.  */
  2920. ENTRY ReturnStatus
  2921. VmPageFlush(virtAddrPtr, length, toDisk, wantRes)
  2922.     Vm_VirtAddr        *virtAddrPtr;
  2923.     int            length;
  2924.     Boolean        toDisk;
  2925.     Boolean        wantRes;
  2926. {
  2927.     VmCore        *corePtr;
  2928.     Fscache_FileInfo    *cacheInfoPtr;
  2929.     ReturnStatus    status = SUCCESS;
  2930.     ReturnStatus    statusTmp;
  2931.     int            firstBlock;
  2932.     Vm_Segment        *segPtr;
  2933.     Fs_Stream        *streamPtr;
  2934.     Vm_PTE        *ptePtr;
  2935.     int            lastPage;
  2936.     int            pageFrame;
  2937.     int            referenced, modified;
  2938.  
  2939.     LOCK_MONITOR;
  2940.     dprintf("VmPageFlush(%x, %d, %d, %d)\n", virtAddrPtr, length, toDisk,
  2941.         wantRes);
  2942.     segPtr = virtAddrPtr->segPtr;
  2943.     lastPage = virtAddrPtr->page + (length>>vmPageShift) - 1;
  2944.     streamPtr = segPtr->swapFilePtr;
  2945.     dprintf("segPtr = %x, firstPage = %d, lastPage = %d, streamPtr = %x\n",
  2946.        segPtr, virtAddrPtr->page, lastPage, streamPtr);
  2947.     for (ptePtr = VmGetAddrPTEPtr(virtAddrPtr, virtAddrPtr->page);
  2948.         virtAddrPtr->page <= lastPage;
  2949.         VmIncPTEPtr(ptePtr,1), virtAddrPtr->page++) {
  2950.     if (!(*ptePtr & VM_PHYS_RES_BIT)) {
  2951.         if (wantRes) {
  2952.         dprintf("Page is not physically resident\n");
  2953.         status = FAILURE;
  2954.         }
  2955.         continue;
  2956.     }
  2957.     pageFrame = Vm_GetPageFrame(*ptePtr);
  2958.     corePtr = &coreMap[pageFrame];
  2959.     referenced = *ptePtr & VM_REFERENCED_BIT;
  2960.     modified = *ptePtr & VM_MODIFIED_BIT;
  2961.     VmMach_AllocCheck(&corePtr->virtPage, pageFrame,
  2962.               &referenced, &modified);
  2963.     if (!modified) {
  2964.         dprintf("Page is clean, so skipping\n");
  2965.         continue;
  2966.     }
  2967.     *ptePtr |= VM_ON_SWAP_BIT;
  2968.     corePtr->flags |= VM_PAGE_BEING_CLEANED;
  2969.     *ptePtr &= ~VM_MODIFIED_BIT;
  2970.     dprintf("VmPageFlush: paging out %d (%d)\n", virtAddrPtr->page, 
  2971.         corePtr-coreMap);
  2972.     VmMach_ClearModBit(&corePtr->virtPage, Vm_GetPageFrame(*ptePtr)); 
  2973.     UNLOCK_MONITOR;
  2974.     statusTmp = VmPageServerWrite(&corePtr->virtPage,
  2975.         (unsigned int)(corePtr-coreMap), toDisk);
  2976.     dprintf("VmPageFlush: status = %x, wrote %x, %x\n", statusTmp,
  2977.         &corePtr->virtPage, (unsigned int)(corePtr-coreMap));
  2978.     LOCK_MONITOR;
  2979.     corePtr->flags &= ~VM_PAGE_BEING_CLEANED;
  2980.     if (statusTmp != SUCCESS) {
  2981.         status = statusTmp;
  2982.         break;
  2983.     }
  2984.     /*
  2985.      * This stuff should probably be in the fs module.
  2986.      */
  2987.     if (streamPtr != (Fs_Stream *)NIL &&
  2988.         streamPtr->ioHandlePtr->fileID.type == FSIO_RMT_FILE_STREAM) {
  2989.         cacheInfoPtr = & ((Fsrmt_FileIOHandle *)streamPtr
  2990.             ->ioHandlePtr)->cacheInfo;
  2991.         if (segPtr->type == VM_STACK) {
  2992.         firstBlock = mach_LastUserStackPage - virtAddrPtr->page;
  2993.         } else if (segPtr->type == VM_SHARED) {
  2994.         firstBlock= virtAddrPtr->page - segOffset(virtAddrPtr) +
  2995.             (virtAddrPtr->sharedPtr->fileAddr>>vmPageShift);
  2996.         } else {
  2997.         firstBlock = virtAddrPtr->page - segPtr->offset;
  2998.         }
  2999.         dprintf("Invalidating block %d\n", firstBlock);
  3000.         Fscache_FileInvalidate(cacheInfoPtr, firstBlock,
  3001.         firstBlock+ (length>>vmPageShift)-1);
  3002.     }
  3003.     }
  3004.     UNLOCK_MONITOR;
  3005.     if (status != SUCCESS) {
  3006.     dprintf("VmPageFlush: failure: %x\n", status);
  3007.     }
  3008.     return status;
  3009. }
  3010.